optiboot_w25x20cl.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // Based on the OptiBoot project
  2. // https://github.com/Optiboot/optiboot
  3. // Licence GLP 2 or later.
  4. #include "Marlin.h"
  5. #include "w25x20cl.h"
  6. #include "stk500.h"
  7. #include "bootapp.h"
  8. #define OPTIBOOT_MAJVER 6
  9. #define OPTIBOOT_CUSTOMVER 0
  10. #define OPTIBOOT_MINVER 2
  11. static unsigned const int __attribute__((section(".version")))
  12. optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER;
  13. /* Watchdog settings */
  14. #define WATCHDOG_OFF (0)
  15. #define WATCHDOG_16MS (_BV(WDE))
  16. #define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
  17. #define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
  18. #define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
  19. #define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
  20. #define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
  21. #define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
  22. #define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
  23. #define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
  24. #define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
  25. #if 0
  26. #define W25X20CL_SIGNATURE_0 9
  27. #define W25X20CL_SIGNATURE_1 8
  28. #define W25X20CL_SIGNATURE_2 7
  29. #else
  30. //FIXME this is a signature of ATmega2560!
  31. #define W25X20CL_SIGNATURE_0 0x1E
  32. #define W25X20CL_SIGNATURE_1 0x98
  33. #define W25X20CL_SIGNATURE_2 0x01
  34. #endif
  35. static void watchdogConfig(uint8_t x) {
  36. WDTCSR = _BV(WDCE) | _BV(WDE);
  37. WDTCSR = x;
  38. }
  39. static void watchdogReset() {
  40. __asm__ __volatile__ (
  41. "wdr\n"
  42. );
  43. }
  44. #define RECV_READY ((UCSR0A & _BV(RXC0)) != 0)
  45. static uint8_t getch(void) {
  46. uint8_t ch;
  47. while(! RECV_READY) ;
  48. if (!(UCSR0A & _BV(FE0))) {
  49. /*
  50. * A Framing Error indicates (probably) that something is talking
  51. * to us at the wrong bit rate. Assume that this is because it
  52. * expects to be talking to the application, and DON'T reset the
  53. * watchdog. This should cause the bootloader to abort and run
  54. * the application "soon", if it keeps happening. (Note that we
  55. * don't care that an invalid char is returned...)
  56. */
  57. watchdogReset();
  58. }
  59. ch = UDR0;
  60. return ch;
  61. }
  62. static void putch(char ch) {
  63. while (!(UCSR0A & _BV(UDRE0)));
  64. UDR0 = ch;
  65. }
  66. static void verifySpace() {
  67. if (getch() != CRC_EOP) {
  68. putch(STK_FAILED);
  69. watchdogConfig(WATCHDOG_16MS); // shorten WD timeout
  70. while (1) // and busy-loop so that WD causes
  71. ; // a reset and app start.
  72. }
  73. putch(STK_INSYNC);
  74. }
  75. static void getNch(uint8_t count) {
  76. do getch(); while (--count);
  77. verifySpace();
  78. }
  79. typedef uint16_t pagelen_t;
  80. static const char entry_magic_send [] PROGMEM = "start\n";
  81. static const char entry_magic_receive[] PROGMEM = "w25x20cl_enter\n";
  82. static const char entry_magic_cfm [] PROGMEM = "w25x20cl_cfm\n";
  83. struct block_t;
  84. extern struct block_t *block_buffer;
  85. void optiboot_w25x20cl_enter()
  86. {
  87. if (boot_app_flags & BOOT_APP_FLG_USER0) return;
  88. uint8_t ch;
  89. uint8_t rampz = 0;
  90. register uint16_t address = 0;
  91. register pagelen_t length;
  92. // Use the planner's queue for the receive / transmit buffers.
  93. // uint8_t *buff = (uint8_t*)block_buffer;
  94. uint8_t buff[260];
  95. // bitmap of pages to be written. Bit is set to 1 if the page has already been erased.
  96. uint8_t pages_erased = 0;
  97. // Handshake sequence: Initialize the serial line, flush serial line, send magic, receive magic.
  98. // If the magic is not received on time, or it is not received correctly, continue to the application.
  99. {
  100. watchdogReset();
  101. unsigned long boot_timeout = 2000000;
  102. unsigned long boot_timer = 0;
  103. const char *ptr = entry_magic_send;
  104. const char *end = strlen_P(entry_magic_send) + ptr;
  105. // Initialize the serial line.
  106. UCSR0A |= (1 << U2X0);
  107. UBRR0L = (((float)(F_CPU))/(((float)(115200))*8.0)-1.0+0.5);
  108. UCSR0B = (1 << RXEN0) | (1 << TXEN0);
  109. // Flush the serial line.
  110. while (RECV_READY) {
  111. watchdogReset();
  112. // Dummy register read (discard)
  113. (void)(*(char *)UDR0);
  114. }
  115. // Send the initial magic string.
  116. while (ptr != end)
  117. putch(pgm_read_byte(ptr ++));
  118. watchdogReset();
  119. // Wait for one second until a magic string (constant entry_magic) is received
  120. // from the serial line.
  121. ptr = entry_magic_receive;
  122. end = strlen_P(entry_magic_receive) + ptr;
  123. while (ptr != end) {
  124. while (! RECV_READY) {
  125. watchdogReset();
  126. delayMicroseconds(1);
  127. if (++ boot_timer > boot_timeout)
  128. // Timeout expired, continue with the application.
  129. return;
  130. }
  131. ch = UDR0;
  132. if (pgm_read_byte(ptr ++) != ch)
  133. // Magic was not received correctly, continue with the application
  134. return;
  135. watchdogReset();
  136. }
  137. // Send the cfm magic string.
  138. ptr = entry_magic_cfm;
  139. while (ptr != end)
  140. putch(pgm_read_byte(ptr ++));
  141. }
  142. spi_init();
  143. w25x20cl_init();
  144. watchdogConfig(WATCHDOG_OFF);
  145. /* Forever loop: exits by causing WDT reset */
  146. for (;;) {
  147. /* get character from UART */
  148. ch = getch();
  149. if(ch == STK_GET_PARAMETER) {
  150. unsigned char which = getch();
  151. verifySpace();
  152. /*
  153. * Send optiboot version as "SW version"
  154. * Note that the references to memory are optimized away.
  155. */
  156. if (which == STK_SW_MINOR) {
  157. putch(optiboot_version & 0xFF);
  158. } else if (which == STK_SW_MAJOR) {
  159. putch(optiboot_version >> 8);
  160. } else {
  161. /*
  162. * GET PARAMETER returns a generic 0x03 reply for
  163. * other parameters - enough to keep Avrdude happy
  164. */
  165. putch(0x03);
  166. }
  167. }
  168. else if(ch == STK_SET_DEVICE) {
  169. // SET DEVICE is ignored
  170. getNch(20);
  171. }
  172. else if(ch == STK_SET_DEVICE_EXT) {
  173. // SET DEVICE EXT is ignored
  174. getNch(5);
  175. }
  176. else if(ch == STK_LOAD_ADDRESS) {
  177. // LOAD ADDRESS
  178. uint16_t newAddress;
  179. // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
  180. // Send the binary data by nibbles to avoid transmitting the ';' character.
  181. newAddress = getch();
  182. newAddress |= getch();
  183. newAddress |= (((uint16_t)getch()) << 8);
  184. newAddress |= (((uint16_t)getch()) << 8);
  185. // Transfer top bit to LSB in rampz
  186. if (newAddress & 0x8000)
  187. rampz |= 0x01;
  188. else
  189. rampz &= 0xFE;
  190. newAddress += newAddress; // Convert from word address to byte address
  191. address = newAddress;
  192. verifySpace();
  193. }
  194. else if(ch == STK_UNIVERSAL) {
  195. // LOAD_EXTENDED_ADDRESS is needed in STK_UNIVERSAL for addressing more than 128kB
  196. if ( AVR_OP_LOAD_EXT_ADDR == getch() ) {
  197. // get address
  198. getch(); // get '0'
  199. rampz = (rampz & 0x01) | ((getch() << 1) & 0xff); // get address and put it in rampz
  200. getNch(1); // get last '0'
  201. // response
  202. putch(0x00);
  203. }
  204. else {
  205. // everything else is ignored
  206. getNch(3);
  207. putch(0x00);
  208. }
  209. }
  210. /* Write memory, length is big endian and is in bytes */
  211. else if(ch == STK_PROG_PAGE) {
  212. // PROGRAM PAGE - we support flash programming only, not EEPROM
  213. uint8_t desttype;
  214. uint8_t *bufPtr;
  215. pagelen_t savelength;
  216. // Read the page length, with the length transferred each nibble separately to work around
  217. // the Prusa's USB to serial infamous semicolon issue.
  218. length = ((pagelen_t)getch()) << 8;
  219. length |= ((pagelen_t)getch()) << 8;
  220. length |= getch();
  221. length |= getch();
  222. savelength = length;
  223. // Read the destination type. It should always be 'F' as flash.
  224. desttype = getch();
  225. // read a page worth of contents
  226. bufPtr = buff;
  227. do *bufPtr++ = getch();
  228. while (--length);
  229. // Read command terminator, start reply
  230. verifySpace();
  231. if (desttype == 'E') {
  232. while (1) ; // Error: wait for WDT
  233. } else {
  234. uint32_t addr = (((uint32_t)rampz) << 16) | address;
  235. // During a single bootloader run, only erase a 64kB block once.
  236. // An 8bit bitmask 'pages_erased' covers 512kB of FLASH memory.
  237. if (address == 0 && (pages_erased & (1 << addr)) == 0) {
  238. w25x20cl_wait_busy();
  239. w25x20cl_enable_wr();
  240. w25x20cl_block64_erase(addr);
  241. pages_erased |= (1 << addr);
  242. }
  243. w25x20cl_wait_busy();
  244. w25x20cl_enable_wr();
  245. w25x20cl_page_program(addr, buff, savelength);
  246. w25x20cl_wait_busy();
  247. w25x20cl_disable_wr();
  248. }
  249. }
  250. /* Read memory block mode, length is big endian. */
  251. else if(ch == STK_READ_PAGE) {
  252. uint32_t addr = (((uint32_t)rampz) << 16) | address;
  253. register pagelen_t i;
  254. // Read the page length, with the length transferred each nibble separately to work around
  255. // the Prusa's USB to serial infamous semicolon issue.
  256. length = ((pagelen_t)getch()) << 8;
  257. length |= ((pagelen_t)getch()) << 8;
  258. length |= getch();
  259. length |= getch();
  260. // Read the destination type. It should always be 'F' as flash. It is not checked.
  261. (void)getch();
  262. verifySpace();
  263. w25x20cl_wait_busy();
  264. w25x20cl_rd_data(addr, buff, length);
  265. for (i = 0; i < length; ++ i)
  266. putch(buff[i]);
  267. }
  268. /* Get device signature bytes */
  269. else if(ch == STK_READ_SIGN) {
  270. // READ SIGN - return what Avrdude wants to hear
  271. verifySpace();
  272. putch(W25X20CL_SIGNATURE_0);
  273. putch(W25X20CL_SIGNATURE_1);
  274. putch(W25X20CL_SIGNATURE_2);
  275. }
  276. else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */
  277. // Adaboot no-wait mod
  278. watchdogConfig(WATCHDOG_16MS);
  279. verifySpace();
  280. }
  281. else {
  282. // This covers the response to commands like STK_ENTER_PROGMODE
  283. verifySpace();
  284. }
  285. putch(STK_OK);
  286. }
  287. }