flashProgrammer.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. // Flash programmer
  2. // Tool to flash the SPI flash chip
  3. #define word char
  4. #include "data/ASCII_BW.c"
  5. #include "lib/math.c"
  6. #include "lib/stdlib.c"
  7. #include "lib/gfx.c"
  8. #define COMMAND_IDLE 0
  9. #define COMMAND_FLASH_READ 114 // 'r'
  10. #define COMMAND_FLASH_ERASE_BLOCK 101 // 'e'
  11. #define COMMAND_FLASH_WRITE 119 // 'w'
  12. #define COMMAND_FILL_BUFFER 98 // 'b'
  13. #define COMMAND_DONE 100 // 'd'
  14. #define BLOCK_SIZE 32768
  15. #define SECTOR_SIZE 4096
  16. #define PAGE_SIZE 256
  17. #define COMMAND_SIZE 8
  18. // the current command the program is busy with
  19. // if idle, should listen to UART for new command
  20. word currentCommand;
  21. // UART buffer that stores a single command
  22. word UARTbufferIndex = 0;
  23. word UARTbuffer[COMMAND_SIZE];
  24. // Buffer of a page to write
  25. word pageBufferIndex = 0;
  26. word pageBuffer[PAGE_SIZE];
  27. /* Description of commands:
  28. READ x(24bit) Bytes from address y(24bit):
  29. b0: 'r'
  30. b1: x[23:16]
  31. b2: x[15:8]
  32. b3: x[7:0]
  33. b4: y[23:16]
  34. b5: y[15:8]
  35. b6: y[7:0]
  36. b7: '\n' (indicate end of command)
  37. */
  38. void initVram()
  39. {
  40. GFX_initVram(); // clear all VRAM
  41. GFX_copyPaletteTable((word)DATA_PALETTE_DEFAULT);
  42. GFX_copyPatternTable((word)DATA_ASCII_DEFAULT);
  43. GFX_cursor = 0;
  44. }
  45. // Workaround for defines in ASM
  46. void SpiFlash_asmDefines()
  47. {
  48. asm(
  49. "define W5500_SPI0_CS_ADDR = 0xC02729 ; address of SPI0_CS\n"
  50. "define W5500_SPI0_ADDR = 0xC02728 ; address of SPI0\n"
  51. );
  52. }
  53. // Sets SPI0_CS low
  54. void SpiBeginTransfer()
  55. {
  56. asm(
  57. "; backup regs\n"
  58. "push r1\n"
  59. "push r2\n"
  60. "load32 W5500_SPI0_CS_ADDR r2 ; r2 = W5500_SPI0_CS_ADDR\n"
  61. "load 0 r1 ; r1 = 0 (enable)\n"
  62. "write 0 r2 r1 ; write to SPI0_CS\n"
  63. "; restore regs\n"
  64. "pop r2\n"
  65. "pop r1\n"
  66. );
  67. }
  68. // Sets SPI0_CS high
  69. void SpiEndTransfer()
  70. {
  71. asm(
  72. "; backup regs\n"
  73. "push r1\n"
  74. "push r2\n"
  75. "load32 W5500_SPI0_CS_ADDR r2 ; r2 = W5500_SPI0_CS_ADDR\n"
  76. "load 1 r1 ; r1 = 1 (disable)\n"
  77. "write 0 r2 r1 ; write to SPI0_CS\n"
  78. "; restore regs\n"
  79. "pop r2\n"
  80. "pop r1\n"
  81. );
  82. }
  83. // write dataByte and return read value
  84. // write 0x00 for a read
  85. // Writes byte over SPI0
  86. word SpiTransfer(word dataByte)
  87. {
  88. word retval = 0;
  89. asm(
  90. "load32 W5500_SPI0_ADDR r2 ; r2 = W5500_SPI0_ADDR\n"
  91. "write 0 r2 r4 ; write r4 over SPI0\n"
  92. "read 0 r2 r2 ; read return value\n"
  93. "write -4 r14 r2 ; write to stack to return\n"
  94. );
  95. return retval;
  96. }
  97. // inits SPI by enabling SPI0 and resetting the chip
  98. void initSPI()
  99. {
  100. // already set CS high before enabling SPI0
  101. SpiEndTransfer();
  102. // enable SPI0
  103. word *p = (word *) 0xC0272A; // set address (SPI0 enable)
  104. *p = 1; // write value
  105. delay(10);
  106. // reset to get out of continuous read mode
  107. SpiBeginTransfer();
  108. SpiTransfer(0x66);
  109. SpiEndTransfer();
  110. delay(1);
  111. SpiBeginTransfer();
  112. SpiTransfer(0x99);
  113. SpiEndTransfer();
  114. delay(1);
  115. SpiBeginTransfer();
  116. SpiTransfer(0x66);
  117. SpiEndTransfer();
  118. delay(1);
  119. SpiBeginTransfer();
  120. SpiTransfer(0x99);
  121. SpiEndTransfer();
  122. delay(1);
  123. }
  124. // Should print 0xEF as Winbond device ID, and 0x17 for W25Q128
  125. void readDeviceID()
  126. {
  127. SpiBeginTransfer();
  128. SpiTransfer(0x90);
  129. SpiTransfer(0);
  130. SpiTransfer(0);
  131. SpiTransfer(0);
  132. word manufacturer = SpiTransfer(0);
  133. word deviceID = SpiTransfer(0);
  134. SpiEndTransfer();
  135. char b[10];
  136. itoah(manufacturer, b);
  137. GFX_PrintConsole("Chip manufacturer: ");
  138. GFX_PrintConsole(b);
  139. GFX_PrintConsole("\n");
  140. GFX_PrintConsole("Chip ID: ");
  141. itoah(deviceID, b);
  142. GFX_PrintConsole(b);
  143. GFX_PrintConsole("\n");
  144. }
  145. void readAndPrint(word len, word addr24, word addr16, word addr8)
  146. {
  147. word l = len;
  148. word a24 = addr24;
  149. word a16 = addr16;
  150. word a8 = addr8;
  151. SpiBeginTransfer();
  152. SpiTransfer(0x03);
  153. SpiTransfer(a24);
  154. SpiTransfer(a16);
  155. SpiTransfer(a8);
  156. word i;
  157. for (i = 0; i < l; i++)
  158. {
  159. uprintc(SpiTransfer(0x00));
  160. }
  161. SpiEndTransfer();
  162. }
  163. /* Returns status register 1 2 or 3, depending on the value of reg (1, 2 or 3)
  164. Status register description:
  165. Register 1: (default: 0x0)
  166. b7: Status Register Protect 0
  167. b6: Sector Protect Bit
  168. b5: Top/Bottom Protect Bit
  169. b4: Block Protect Bit 2
  170. b3: Block Protect Bit 1
  171. b2: Block Protect Bit 0
  172. b1: Write Enable Latch
  173. b0: BUSY: Erase/Write in Progress
  174. Register 2: (default: 0x2)
  175. b7: Suspend Status
  176. b6: Complement Protect
  177. b5: Security Register Lock bit 2
  178. b4: Security Register Lock bit 1
  179. b3: Security Register Lock bit 0
  180. b2: Reserved
  181. b1: Quad Enable
  182. b0: Status Register Protect 1
  183. Register 3: (default: 0x60)
  184. b7: Reserved
  185. b6: Output Driver Strength bit 1
  186. b5: Output Driver Strength bit 0
  187. b4: Reserved
  188. b3: Reserved
  189. b2: Write Protect Selection
  190. b1: Reserved
  191. b0: Reserved
  192. */
  193. word readStatusRegister(word reg)
  194. {
  195. SpiBeginTransfer();
  196. if (reg == 2)
  197. SpiTransfer(0x35);
  198. else if (reg == 3)
  199. SpiTransfer(0x15);
  200. else
  201. SpiTransfer(0x05);
  202. word status = SpiTransfer(0);
  203. SpiEndTransfer();
  204. return status;
  205. }
  206. void enableWrite()
  207. {
  208. SpiBeginTransfer();
  209. SpiTransfer(0x06);
  210. SpiEndTransfer();
  211. }
  212. // Executes chip erase operation
  213. // TAKES AT LEAST 20 SECONDS!
  214. // Returns 1 on success
  215. word chipErase()
  216. {
  217. enableWrite();
  218. word status = readStatusRegister(1);
  219. // Check if write is enabled
  220. if ((status & 0x2) == 0)
  221. return 0;
  222. // Send command
  223. SpiBeginTransfer();
  224. SpiTransfer(0xC7);
  225. SpiEndTransfer();
  226. // Wait for busy bit to be 0
  227. status = 1;
  228. while((status & 0x1) == 1)
  229. {
  230. status = readStatusRegister(1);
  231. }
  232. return 1;
  233. }
  234. // Executes 32KiB block erase operation
  235. // Erases the 32KiB block that contains the given byte address
  236. // Returns 1 on success
  237. word blockErase(word addr24, word addr16, word addr8)
  238. {
  239. word a24 = addr24;
  240. word a16 = addr16;
  241. word a8 = addr8;
  242. enableWrite();
  243. word status = readStatusRegister(1);
  244. // Check if write is enabled
  245. if ((status & 0x2) == 0)
  246. return 0;
  247. // Send command
  248. SpiBeginTransfer();
  249. SpiTransfer(0x52);
  250. SpiTransfer(a24);
  251. SpiTransfer(a16);
  252. SpiTransfer(a8);
  253. SpiEndTransfer();
  254. // Wait for busy bit to be 0
  255. status = 1;
  256. while((status & 0x1) == 1)
  257. {
  258. status = readStatusRegister(1);
  259. }
  260. return 1;
  261. }
  262. // Executes 256 byte page program operation
  263. // The address is the byte address of the first byte to write
  264. // Data wraps around at the end of a page,
  265. // so last address byte will usually be 0
  266. // Returns 1 on success
  267. word pageProgram(word addr24, word addr16, word addr8)
  268. {
  269. word a24 = addr24;
  270. word a16 = addr16;
  271. word a8 = addr8;
  272. enableWrite();
  273. word status = readStatusRegister(1);
  274. // Check if write is enabled
  275. if ((status & 0x2) == 0)
  276. return 0;
  277. // Send command
  278. SpiBeginTransfer();
  279. SpiTransfer(0x2);
  280. SpiTransfer(a24);
  281. SpiTransfer(a16);
  282. SpiTransfer(a8);
  283. // Send page
  284. word *p_pageBuffer = pageBuffer;
  285. word i;
  286. for (i = 0; i < 256; i++)
  287. {
  288. SpiTransfer(*(p_pageBuffer + i));
  289. }
  290. SpiEndTransfer();
  291. // Wait for busy bit to be 0
  292. status = 1;
  293. while((status & 0x1) == 1)
  294. {
  295. status = readStatusRegister(1);
  296. }
  297. return 1;
  298. }
  299. void processCommand()
  300. {
  301. if (currentCommand == COMMAND_IDLE)
  302. {
  303. // do nothing
  304. }
  305. else if (currentCommand == COMMAND_FLASH_READ)
  306. {
  307. word len = UARTbuffer[1] << 16;
  308. len += UARTbuffer[2] << 8;
  309. len += UARTbuffer[3];
  310. word addr24 = UARTbuffer[4];
  311. word addr16 = UARTbuffer[5];
  312. word addr8 = UARTbuffer[6];
  313. GFX_printWindowColored(" ", 40, GFX_WindowPosFromXY(0, 8), 0);
  314. GFX_printWindowColored("Reading ", 8, GFX_WindowPosFromXY(0, 8), 0);
  315. char b[10];
  316. itoa(len, b);
  317. GFX_printWindowColored(b, strlen(b), GFX_WindowPosFromXY(8, 8), 0);
  318. GFX_printWindowColored(" bytes", 6, GFX_WindowPosFromXY(8 + strlen(b), 8), 0);
  319. readAndPrint(len, addr24, addr16, addr8);
  320. GFX_printWindowColored(" done!", 5, GFX_WindowPosFromXY(8 + strlen(b) + 6, 8), 0);
  321. currentCommand = COMMAND_IDLE;
  322. uprintc('r');
  323. }
  324. else if (currentCommand == COMMAND_FLASH_ERASE_BLOCK)
  325. {
  326. word addr24 = UARTbuffer[4];
  327. word addr16 = UARTbuffer[5];
  328. word addr8 = UARTbuffer[6];
  329. blockErase(addr24, addr16, addr8);
  330. word addrCombined = ((addr24 << 16) + (addr16 << 8) + addr8) >> 15;
  331. GFX_printWindowColored(" ", 40, GFX_WindowPosFromXY(0, 8), 0);
  332. GFX_printWindowColored("Block ", 6, GFX_WindowPosFromXY(0, 8), 0);
  333. char b[10];
  334. itoa(addrCombined, b);
  335. GFX_printWindowColored(b, strlen(b), GFX_WindowPosFromXY(6, 8), 0);
  336. GFX_printWindowColored(" erased", 7, GFX_WindowPosFromXY(6 + strlen(b), 8), 0);
  337. currentCommand = COMMAND_IDLE;
  338. uprintc('r');
  339. }
  340. else if (currentCommand == COMMAND_FLASH_WRITE)
  341. {
  342. word addr24 = UARTbuffer[4];
  343. word addr16 = UARTbuffer[5];
  344. word addr8 = UARTbuffer[6];
  345. pageProgram(addr24, addr16, addr8);
  346. word addrCombined = (addr24 << 8) + addr16;
  347. GFX_printWindowColored(" ", 40, GFX_WindowPosFromXY(0, 8), 0);
  348. GFX_printWindowColored("Page ", 5, GFX_WindowPosFromXY(0, 8), 0);
  349. char b[10];
  350. itoa(addrCombined, b);
  351. GFX_printWindowColored(b, strlen(b), GFX_WindowPosFromXY(5, 8), 0);
  352. GFX_printWindowColored(" written", 8, GFX_WindowPosFromXY(5 + strlen(b), 8), 0);
  353. currentCommand = COMMAND_IDLE;
  354. uprintc('r');
  355. }
  356. else if (currentCommand == COMMAND_FILL_BUFFER)
  357. {
  358. // done when full page is received
  359. if (pageBufferIndex == PAGE_SIZE)
  360. {
  361. pageBufferIndex = 0;
  362. currentCommand = COMMAND_IDLE;
  363. uprintc('r');
  364. }
  365. }
  366. else if (currentCommand == COMMAND_DONE)
  367. {
  368. // notify done
  369. GFX_printWindowColored(" ", 40, GFX_WindowPosFromXY(0, 6), 0);
  370. GFX_printWindowColored(" ", 40, GFX_WindowPosFromXY(0, 8), 0);
  371. GFX_printWindowColored("Done!", 5, GFX_WindowPosFromXY(0, 6), 0);
  372. // allow new commands, just in case
  373. currentCommand = COMMAND_IDLE;
  374. }
  375. }
  376. int main()
  377. {
  378. initVram();
  379. GFX_PrintConsole("Flash Programmer\n\n");
  380. currentCommand = COMMAND_IDLE;
  381. initSPI();
  382. GFX_PrintConsole("SPI0 mode set\n");
  383. readDeviceID();
  384. GFX_PrintConsole("\nReady to receive commands\n\n");
  385. // Notify that we are ready to recieve commands
  386. uprintc('r');
  387. while (1)
  388. {
  389. processCommand();
  390. }
  391. return 48;
  392. }
  393. void int1()
  394. {
  395. /*
  396. word *p = (word *) 0x4C0000; // set address (timer1 state)
  397. *p = 1; // write value
  398. */
  399. timer1Value = 1; // notify ending of timer1
  400. }
  401. void int2()
  402. {
  403. }
  404. void int3()
  405. {
  406. // UART RX interrupt
  407. // Fill buffer
  408. if (currentCommand == COMMAND_FILL_BUFFER)
  409. {
  410. // read byte
  411. word *p = (word *)0xC02722; // address of UART RX
  412. word b = *p; // read byte from UART
  413. // write byte to buffer, increase index
  414. word *p_pageBuffer = pageBuffer;
  415. *(p_pageBuffer + pageBufferIndex) = b;
  416. pageBufferIndex++;
  417. //uprintc('a'); // notify write to buffer is done
  418. }
  419. // Get command
  420. else if (currentCommand == COMMAND_IDLE)
  421. {
  422. // read byte
  423. word *p = (word *)0xC02722; // address of UART RX
  424. word b = *p; // read byte from UART
  425. // write byte to buffer, increase index
  426. word *p_UARTbuffer = UARTbuffer;
  427. *(p_UARTbuffer + UARTbufferIndex) = b;
  428. UARTbufferIndex++;
  429. // execute command when 8 bytes received
  430. if (UARTbufferIndex == COMMAND_SIZE)
  431. {
  432. currentCommand = *(p_UARTbuffer);
  433. UARTbufferIndex = 0;
  434. // notify ready to receive when filling buffer
  435. if (currentCommand == COMMAND_FILL_BUFFER)
  436. {
  437. uprintc('b');
  438. }
  439. }
  440. }
  441. }
  442. void int4()
  443. {
  444. }
  445. void interrupt()
  446. {
  447. word i = getIntID();
  448. if (i == 1)
  449. {
  450. int1();
  451. }
  452. else if (i == 3)
  453. {
  454. int3();
  455. }
  456. }