Project

General

Profile

Statistics
| Revision:

root / trunk / toolbox / bootloader / bootloader.c @ 176

History | View | Annotate | Download (6.97 KB)

1
#include <avr/io.h>
2
#include <avr/boot.h>
3
#include <avr/pgmspace.h>
4
#include <avr/wdt.h>
5

    
6
#include "tooltron.h"
7
#include "uart.h"
8

    
9
#define TRUE        0
10
#define FALSE        1
11

    
12
#define ADDR    18
13

    
14

    
15
// Error thresholds
16
#define MAX_TIMEOUT 60000   // Seconds to wait before exiting bootloader mode
17
#define MAX_RETRIES 5       // Number of times to retry before giving up
18

    
19
// Memory locations
20
#define MAIN_ADDR 0x000     // Where the user code starts
21
#define BOOT_START 0x400    // Where the bootloader starts
22

    
23
//Status LED
24
#define LED_DDR  DDRB
25
#define LED_PORT PORTB
26
#define LED      PORTB1
27

    
28
//Function prototypes
29
void (*main_start)(void) = BOOT_START/2 - 1;
30

    
31
// Packet handler states
32
typedef enum {
33
    sd,
34
    src,
35
    dest,
36
    comd,
37
    read,
38
    cs,
39
    ack
40
} state_t;
41

    
42
typedef union {
43
    uint8_t bytes[2];
44
    int16_t sword;
45
} rjump_t;
46

    
47
void init_uart(uint16_t baud) {
48
        // Set baud rate
49
        UBRRH = (uint8_t)(baud>>8);
50
        UBRRL = (uint8_t)baud;
51
        
52
        // Enable RX/TX
53
        UCSRB = _BV(RXEN) | _BV(TXEN);
54
 
55
        // Enable the TXEN pin as output
56
        DDRD |= TX_EN;
57
    uart_toggle_transmit(UART_TX_OFF);
58
}
59

    
60
int8_t uart_get_byte(uint8_t *output_byte) {
61
    if (UCSRA & _BV(RXC)) {
62
        *output_byte = UDR;
63
        return 0;
64
    } else {
65
        return -1;
66
    }
67
}
68

    
69
void uart_send_byte(uint8_t data) {
70
        //Waits until current transmit is done
71
    while (!(UCSRA & _BV(UDRE)));
72

    
73
    // Enable writes and send
74
        uart_toggle_transmit(UART_TX_ON);
75
    UDR = data;
76

    
77
    // Waits until the transmit is done
78
    while(!(UCSRA & _BV(TXC)));
79
    uart_toggle_transmit(UART_TX_OFF);
80
    UCSRA |= _BV(TXC);
81

    
82
        return;
83
}
84

    
85
void uart_toggle_transmit(uint8_t state) {
86
        if (state == UART_TX_ON) {
87
                PORTD |= TX_EN;
88
        } else {
89
                PORTD &= ~TX_EN;
90
        }
91
}
92

    
93
char parse_packet(uint8_t *mbuf) {
94
  uint8_t r;        // Byte from the network
95
  uint8_t crc;      // Running checksum of the packet
96
  uint8_t cmd;      // The command received
97
  uint8_t pos;      // Position in the message buffer
98
  uint8_t lim;      // Max number of bytes to read into the message buf
99
  state_t state;    // State machine
100
  uint16_t count;
101

    
102
  r = 0;
103
  crc = 0;
104
  cmd = 0;
105
  pos = 0;
106
  lim = 0;
107
  state = sd;
108
  count = 0;
109

    
110
  while (1) {
111
    // Wait for the next byte
112
    while ((uart_get_byte(&r)) < 0) {
113
        if (count >= MAX_TIMEOUT) {
114
            return TT_BAD;
115
        } else {
116
            count++;
117
        }
118
    }
119

    
120
    switch (state) {
121
        case sd:
122
            if (r == DELIM) {
123
                state = src;
124
            }
125
            break;
126

    
127
        case src:
128
            if (r == DELIM) {
129
                state = src;
130
            } else {
131
                crc = r;
132
                state = dest;
133
            }
134
            break;
135

    
136
        case dest:
137
            if (r == DELIM) {
138
                state = src;
139
            } else if (r == ADDR) {
140
                crc ^= r;
141
                state = comd;
142
            } else {
143
                state = sd;
144
            }
145
            break;
146

    
147
        case comd:
148
            cmd = r;
149
            crc ^= r;
150

    
151
            if (r == DELIM) {
152
                state = src;
153
            } else if (r == TT_PROGM) {
154
                lim = PROGM_PACKET_SIZE;
155
                state = read;
156
            } else if (r == TT_PROGD) {
157
                lim = PROGD_PACKET_SIZE;
158
                state = read;
159
            } else {
160
                state = cs;
161
            }
162
            break;
163

    
164
        case read:
165
            mbuf[pos] = r;
166
            crc ^= r;
167
            pos++;
168

    
169
            if (pos == lim) {
170
                state = cs;
171
            }
172

    
173
            break;
174
       
175
        case cs:
176
            if (r == crc) {
177
                return cmd;
178
            } else {
179
                return TT_BAD;
180
            }
181

    
182
            break;
183

    
184
        default:
185
            return TT_BAD;
186
    }
187
  }
188
}
189

    
190
void send_packet(uint8_t cmd) {
191
    uart_send_byte(DELIM);
192
    uart_send_byte(ADDR);
193
    uart_send_byte(SERVER);
194
    uart_send_byte(cmd);
195
    uart_send_byte(ACK_CRC ^ cmd);
196
}
197

    
198
// SPM_PAGESIZE is set to 32 bytes
199
void onboard_program_write(uint16_t page, uint8_t *buf) {
200
  uint16_t i;
201

    
202
  boot_page_erase (page);
203
  boot_spm_busy_wait ();      // Wait until the memory is erased.
204

    
205
  for (i=0; i < SPM_PAGESIZE; i+=2){
206
    // Set up little-endian word.
207
    boot_page_fill (page + i, buf[i] | (buf[i+1] <<8));
208
  }
209

    
210
  boot_page_write (page);     // Store buffer in flash page.
211
  boot_spm_busy_wait();       // Wait until the memory is written.
212
}
213

    
214
int main(void) {
215
  uint8_t mbuf[PROGD_PACKET_SIZE];
216
  rjump_t jbuf;
217
  uint16_t caddr = MAIN_ADDR;
218
  uint8_t iteration;
219
  uint8_t resp;
220
  uint16_t prog_len;
221
  uint8_t i;
222
  uint8_t retries;
223

    
224
retry_jpnt:
225
  iteration = 0;
226
  retries = 0;
227
  
228
  // Clear the watchdog timer
229
  MCUSR &= ~_BV(WDRF);
230
  wdt_disable();
231
  WDTCSR = 0;
232

    
233

    
234
  init_uart(51); //MAGIC NUMBER??
235

    
236
  //set LED pin as output
237
  LED_DDR |= 0x07;
238
  PORTB = 0x07;
239

    
240
  //Start bootloading process
241
  send_packet(TT_BOOT);
242

    
243
  resp = parse_packet(mbuf);
244

    
245
  // Enter programming mode 
246
  if (resp == TT_PROGM) {
247
    prog_len = mbuf[0];
248
    prog_len |= mbuf[1] << 8;
249

    
250
    // This will insert a NOP into the user code jump in case
251
    // the programming fails
252
    for (i = 0; i < PROGD_PACKET_SIZE; i++) {
253
      mbuf[i]= 0;
254
    }
255
    onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
256

    
257
  // Run user code
258
  } else {
259
      main_start();
260
  }
261

    
262
  send_packet(TT_ACK);
263

    
264
  while(1) {
265
      resp = parse_packet(mbuf);
266

    
267
      if (resp == TT_PROGD) {
268
          // We need to muck with the reset vector jump in the first page
269
          if (iteration == 0) {
270
              // Store the jump to user code
271
              jbuf.bytes[0] = mbuf[0];
272
              jbuf.bytes[1] = mbuf[1];
273
             
274
              // Rewrite the user code jump to be correct since we are
275
              // using relative jumps (rjmp)
276
              jbuf.sword &= 0x0FFF;
277
              jbuf.sword -= (BOOT_START >> 1) - 1;
278
              jbuf.sword &= 0x0FFF;
279
              jbuf.sword |= 0xC000;
280

    
281
              // Rewrite the reset vector to jump to the bootloader
282
              mbuf[0] = (BOOT_START/2 - 1) & 0xFF;
283
              mbuf[1] = 0xC0 | (((BOOT_START/2 - 1) >> 8) & 0x0F);
284

    
285
              iteration = 1;
286
          }
287

    
288
          // Write the page to the flash
289
          onboard_program_write(caddr, mbuf);
290
          caddr += PROGD_PACKET_SIZE;
291
          retries = 0;
292
      } else {
293
          send_packet(TT_NACK);
294
          retries++;
295

    
296
          // If we failed too many times, reset. This goes to the start
297
          // of the bootloader function
298
          if (retries > MAX_RETRIES) {
299
              goto retry_jpnt;
300
          }
301
      }
302

    
303
      send_packet(TT_ACK);
304

    
305
      // Once we write the last packet we must override the jump to
306
      // user code to point to the correct address
307
      if (prog_len <= PROGD_PACKET_SIZE) {
308
          for (i = 0; i < PROGD_PACKET_SIZE; i++) {
309
              mbuf[i]= 0;
310
          }
311

    
312
          mbuf[PROGD_PACKET_SIZE-2] = jbuf.bytes[0];
313
          mbuf[PROGD_PACKET_SIZE-1] = jbuf.bytes[1];
314

    
315
          onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
316

    
317
          main_start();
318
      } else { 
319
          prog_len -= PROGD_PACKET_SIZE;
320
      }
321
  }
322

    
323
  // Should never get here
324
  return -1;
325
}