Skip to content

Commit 7e1109e

Browse files
author
Nathan Seidle
committed
Add Artemis Bootloader
In case people over-write the SparkFun bootloader this is a handy example to have in the sub menu.
1 parent f21caaa commit 7e1109e

File tree

1 file changed

+320
-0
lines changed

1 file changed

+320
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
/*
2+
Artemis Bootloader
3+
By: Nathan Seidle
4+
SparkFun Electronics
5+
Date: May 2nd, 2019
6+
License: MIT. See license file for more information but you can
7+
basically do whatever you want with this code.
8+
9+
This is a serial bootloader that receives commands from a python script and loads
10+
user's code as quickly as possible.
11+
12+
Feel like supporting open source hardware?
13+
Buy a board from SparkFun!
14+
SparkFun Edge: https://www.sparkfun.com/products/15170
15+
16+
Based on SparkFun XBee Bootloader from 2009: https://www.sparkfun.com/tutorials/122
17+
18+
The Apollo3 memory looks like this:
19+
20+
0xFF000 -> -------
21+
| |
22+
| |
23+
...
24+
| |
25+
| |
26+
| |
27+
0x10000 -> | APP |
28+
| SFE |
29+
0xC000 -> | SBL |
30+
-------
31+
32+
The Ambiq secure bootloader (SBL) lives at 0x00 to 0xC000.
33+
The SparkFun Artemis bootloader begins at 0xC000 and takes ~7300 bytes (with debug statements it's ~10k)
34+
We load user code starting at 0xC000 + 16384 = 0x10000
35+
If our bootloader completes or times out, we jump to 0x10000+4 to begin running user code
36+
*/
37+
38+
#define BOOTLOADER_VERSION 0x01
39+
40+
static uint32_t ui32Source[512];
41+
42+
#define MAX_WAIT_IN_MS 50 //Millisecond wait before bootload timeout and begin user code
43+
44+
enum BL_COMMANDS {
45+
BL_COMMAND_ANNOUNCE = 127, //Delete
46+
BL_COMMAND_AOK = 0x06,
47+
BL_COMMAND_BAD_CRC,
48+
BL_COMMAND_NEXT_FRAME,
49+
BL_COMMAND_ALL_DONE,
50+
BL_COMMAND_COMPUTER_READY,
51+
};
52+
53+
bool retransmit_flag = false;
54+
uint16_t frame_size = 0;
55+
uint32_t incoming_crc32 = 0;
56+
uint32_t crc32 = 0;
57+
uint32_t frame_address = 0;
58+
uint16_t last_page_erased = 0;
59+
60+
// Location in flash to begin storing user's code
61+
// Linker script needs to be adjusted to offset user's flash to this address
62+
#define USERCODE_OFFSET 0xC000 + 0x4000
63+
64+
//Comment out this line to turn off debug statements on Serial1
65+
//#define DEBUG 1
66+
67+
void setup() {
68+
Serial.begin(9600); //Start comm with computer at low baud rate. We'll go faster later.
69+
70+
#ifdef DEBUG
71+
Serial1.begin(921600);
72+
Serial1.println();
73+
Serial1.println();
74+
Serial1.println("Debug");
75+
#endif
76+
77+
delay(3); //Necessary to separate a blip on TX line when port opens from annouce command
78+
Serial.write(BL_COMMAND_ANNOUNCE); //Tell the world we can be bootloaded
79+
80+
//Check to see if the computer responded
81+
uint16_t count = 0;
82+
while (Serial.available() == 0)
83+
{
84+
if (count++ > MAX_WAIT_IN_MS)
85+
{
86+
#ifdef DEBUG
87+
Serial1.println("No response from computer");
88+
#endif
89+
app_start();
90+
}
91+
delay(1);
92+
}
93+
if (Serial.read() != BL_COMMAND_AOK)
94+
{
95+
#ifdef DEBUG
96+
Serial1.println("Invalid response from computer");
97+
#endif
98+
app_start(); //If the computer did not respond correctly with a AOK, we jump to user's program
99+
}
100+
101+
//Now get upload baud rate from caller
102+
uint32_t uploadRate = get4Bytes();
103+
104+
Serial.write(BOOTLOADER_VERSION);
105+
delay(5); //Small delay before baud change. Otherwise baud fails to change.
106+
107+
Serial.begin(uploadRate); //Go to new baud rate
108+
109+
delay(10); //Give the computer some time to switch baud rates
110+
111+
#ifdef DEBUG
112+
Serial1.print("Bootloading @ ");
113+
Serial1.println(uploadRate);
114+
#endif
115+
}
116+
117+
//This is the main bootloader
118+
//We'll loop until the whole file is received
119+
void loop() {
120+
//Determine if the last received data was good or bad
121+
if (crc32 != incoming_crc32)
122+
{
123+
RESTART:
124+
Serial.write(BL_COMMAND_BAD_CRC); //Tell client to resend last frame
125+
#ifdef DEBUG
126+
Serial1.println("RESTART!");
127+
#endif
128+
}
129+
else
130+
{
131+
Serial.write(BL_COMMAND_NEXT_FRAME); //Get next frame
132+
#ifdef DEBUG
133+
Serial1.println();
134+
Serial1.println("Getting next frame");
135+
#endif
136+
}
137+
138+
while (1) //Wait for the computer to tell us its ready to chat
139+
{
140+
if (getch() == BL_COMMAND_COMPUTER_READY) break; //This is the "gimme the next chunk" command
141+
if (retransmit_flag == true) goto RESTART;
142+
}
143+
144+
frame_size = ((uint16_t)getch() << 8) | getch(); //Get the size of this frame
145+
if (retransmit_flag == true)goto RESTART;
146+
147+
if (frame_size == BL_COMMAND_ALL_DONE) //Check to see if we are done
148+
{
149+
#ifdef DEBUG
150+
Serial1.println("Done!");
151+
#endif
152+
am_hal_reset_control(AM_HAL_RESET_CONTROL_SWPOI, 0); //Cause a system Power On Init to release as much of the stack as possible
153+
}
154+
155+
#ifdef DEBUG
156+
Serial1.print("frame_size: ");
157+
Serial1.println(frame_size);
158+
#endif
159+
160+
//Get the memory address at which to store this block of data
161+
frame_address = get4Bytes();
162+
if (retransmit_flag == true) goto RESTART;
163+
164+
//Serial1.print("frame_address: ");
165+
//Serial1.println(frame_address);
166+
167+
//Get CRC from client
168+
incoming_crc32 = get4Bytes();
169+
if (retransmit_flag == true) goto RESTART;
170+
171+
//Serial1.print("incoming_crc32: ");
172+
//Serial1.println(incoming_crc32);
173+
174+
//Receive this frame
175+
crc32 = 0;
176+
for (uint16_t i = 0 ; i < frame_size / 4 ; i++)
177+
{
178+
ui32Source[i] = get4BytesReversed();
179+
crc32 += ui32Source[i]; //Calculate CRC as we go
180+
}
181+
182+
if (incoming_crc32 == crc32)
183+
{
184+
#ifdef DEBUG
185+
Serial1.println("CRC good.");
186+
#endif
187+
188+
int32_t i32ReturnCode = 0;
189+
190+
//Frames coming from the computer are 2k bytes, but we erase 8k bytes in a page
191+
//Only erase a page if we haven't erased it before
192+
if (last_page_erased < AM_HAL_FLASH_ADDR2PAGE(frame_address + USERCODE_OFFSET))
193+
{
194+
#ifdef DEBUG
195+
Serial1.printf("Erasing instance %d, page %d\n\r", AM_HAL_FLASH_ADDR2INST(frame_address + USERCODE_OFFSET), AM_HAL_FLASH_ADDR2PAGE(frame_address + USERCODE_OFFSET));
196+
#endif
197+
198+
//Erase the 8k page for this address
199+
i32ReturnCode = am_hal_flash_page_erase(AM_HAL_FLASH_PROGRAM_KEY,
200+
AM_HAL_FLASH_ADDR2INST(frame_address + USERCODE_OFFSET),
201+
AM_HAL_FLASH_ADDR2PAGE(frame_address + USERCODE_OFFSET) );
202+
last_page_erased = AM_HAL_FLASH_ADDR2PAGE(frame_address + USERCODE_OFFSET);
203+
}
204+
205+
#ifdef DEBUG
206+
if (i32ReturnCode)
207+
{
208+
Serial1.printf("FLASH_MASS_ERASE i32ReturnCode = 0x%x.\n\r", i32ReturnCode);
209+
}
210+
#endif
211+
212+
//Record the array
213+
i32ReturnCode = program_array(frame_address + USERCODE_OFFSET);
214+
215+
#ifdef DEBUG
216+
if (i32ReturnCode)
217+
Serial1.printf("FLASH_WRITE error = 0x%x.\n\r", i32ReturnCode);
218+
else
219+
Serial1.println("Array recorded to flash");
220+
#endif
221+
}
222+
else
223+
{
224+
#ifdef DEBUG
225+
Serial1.println("Bad CRC:");
226+
Serial1.printf("incoming_crc32: 0x%04X\n\r", incoming_crc32);
227+
Serial1.printf("CRC: 0x%04X\n\r", crc32);
228+
#endif
229+
}
230+
}
231+
232+
//Gets a character from Serial
233+
uint8_t getch()
234+
{
235+
retransmit_flag = false;
236+
237+
uint16_t counter = 0;
238+
while (Serial.available() == 0)
239+
{
240+
delayMicroseconds(10);
241+
if (counter++ > 10000)
242+
{
243+
retransmit_flag = true;
244+
return (0); //Timeout
245+
}
246+
}
247+
return (Serial.read());
248+
}
249+
250+
uint32_t get4Bytes(void)
251+
{
252+
uint32_t incoming = 0;
253+
for (byte x = 0 ; x < 4 ; x++)
254+
{
255+
incoming <<= 8;
256+
incoming |= getch();
257+
}
258+
return (incoming);
259+
}
260+
261+
uint32_t get4BytesReversed(void)
262+
{
263+
uint32_t incoming = 0;
264+
for (byte x = 0 ; x < 4 ; x++)
265+
{
266+
incoming >>= 8;
267+
incoming |= (getch() << 8 * 3);
268+
}
269+
return (incoming);
270+
}
271+
272+
void app_start()
273+
{
274+
#ifdef DEBUG
275+
// Print a section of flash
276+
uint32_t start_address = USERCODE_OFFSET;
277+
Serial1.printf("Printing page starting at offset 0x%04X", start_address);
278+
for (uint16_t x = 0; x < 512; x++)
279+
{
280+
if (x % 8 == 0)
281+
{
282+
Serial1.println();
283+
Serial1.printf("Adr: 0x%04X", start_address + (x * 4));
284+
}
285+
Serial1.printf(" 0x%08X", *(uint32_t *)(start_address + (x * 4)));
286+
}
287+
Serial1.println();
288+
#endif
289+
290+
uint32_t entryPoint = *(uint32_t *)(USERCODE_OFFSET + 4);
291+
292+
#ifdef DEBUG
293+
Serial1.printf("Jump to 0x%08X", entryPoint);
294+
#endif
295+
delay(10); //Wait for prints to complete
296+
297+
goto *entryPoint; //Jump to start of user code
298+
299+
#ifdef DEBUG
300+
Serial1.println("Not here");
301+
#endif
302+
while (1);
303+
}
304+
305+
//Commits whatever is in the ui32Source to the given location
306+
int32_t program_array(uint32_t ui32PrgmAddr)
307+
{
308+
uint32_t *pui32Dst;
309+
int32_t i32ReturnCode;
310+
//
311+
// Program a few words in a page in the main block of instance 1.
312+
//
313+
pui32Dst = (uint32_t *) ui32PrgmAddr;
314+
i32ReturnCode = am_hal_flash_program_main(AM_HAL_FLASH_PROGRAM_KEY,
315+
ui32Source,
316+
pui32Dst,
317+
512);
318+
319+
return (i32ReturnCode);
320+
}

0 commit comments

Comments
 (0)