Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (6.86 KB)

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

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

    
8
#define TRUE        0
9
#define FALSE        1
10

    
11
#define ADDR    18
12

    
13

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

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

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

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

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

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

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

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

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

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

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

    
81
        return;
82
}
83

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

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

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

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

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

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

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

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

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

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

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

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

    
181
            break;
182

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

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

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

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

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

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

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

    
227
  init_uart(51); //MAGIC NUMBER??
228

    
229
  //set LED pin as output
230
  LED_DDR |= 0x07;
231
  PORTB = 0x07;
232

    
233
  //Start bootloading process
234
  send_packet(TT_BOOT);
235

    
236
  resp = parse_packet(mbuf);
237

    
238
  // Enter programming mode 
239
  if (resp == TT_PROGM) {
240
    prog_len = mbuf[0];
241
    prog_len |= mbuf[1] << 8;
242

    
243
    // This will insert a NOP into the user code jump in case
244
    // the programming fails
245
    for (i = 0; i < PROGD_PACKET_SIZE; i++) {
246
      mbuf[i]= 0;
247
    }
248
    onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
249

    
250
  // Run user code
251
  } else {
252
      main_start();
253
  }
254

    
255
  send_packet(TT_ACK);
256

    
257
  while(1) {
258
      resp = parse_packet(mbuf);
259

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

    
274
              // Rewrite the reset vector to jump to the bootloader
275
              mbuf[0] = (BOOT_START/2 - 1) & 0xFF;
276
              mbuf[1] = 0xC0 | (((BOOT_START/2 - 1) >> 8) & 0x0F);
277

    
278
              iteration = 1;
279
          }
280

    
281
          // Write the page to the flash
282
          onboard_program_write(caddr, mbuf);
283
          caddr += PROGD_PACKET_SIZE;
284
          retries = 0;
285
      } else {
286
          send_packet(TT_NACK);
287
          retries++;
288

    
289
          // If we failed too many times, reset. This goes to the start
290
          // of the bootloader function
291
          if (retries > MAX_RETRIES) {
292
              goto retry_jpnt;
293
          }
294
      }
295

    
296
      send_packet(TT_ACK);
297

    
298
      // Once we write the last packet we must override the jump to
299
      // user code to point to the correct address
300
      if (prog_len <= PROGD_PACKET_SIZE) {
301
          for (i = 0; i < PROGD_PACKET_SIZE; i++) {
302
              mbuf[i]= 0;
303
          }
304

    
305
          mbuf[PROGD_PACKET_SIZE-2] = jbuf.bytes[0];
306
          mbuf[PROGD_PACKET_SIZE-1] = jbuf.bytes[1];
307

    
308
          onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
309

    
310
          main_start();
311
      } else { 
312
          prog_len -= PROGD_PACKET_SIZE;
313
      }
314
  }
315

    
316
  // Should never get here
317
  return -1;
318
}