spiflash.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /*
  2. * SPI Flash library
  3. * Contains functions to interact with the Winbond W25Q128 SPI Flash chip
  4. */
  5. // Sets SPI0_CS low
  6. void spiflash_begin_transfer()
  7. {
  8. asm(
  9. "; Backup regs\n"
  10. "push r1\n"
  11. "push r2\n"
  12. "load32 0xC02729 r2 ; r2 = 0xC02729\n"
  13. "load 0 r1 ; r1 = 0 (enable)\n"
  14. "write 0 r2 r1 ; write to SPI0_CS\n"
  15. "; Restore regs\n"
  16. "pop r2\n"
  17. "pop r1\n"
  18. );
  19. }
  20. // Sets SPI0_CS high
  21. void spiflash_end_transfer()
  22. {
  23. asm(
  24. "; Backup regs\n"
  25. "push r1\n"
  26. "push r2\n"
  27. "load32 0xC02729 r2 ; r2 = 0xC02729\n"
  28. "load 1 r1 ; r1 = 1 (disable)\n"
  29. "write 0 r2 r1 ; write to SPI0_CS\n"
  30. "; Restore regs\n"
  31. "pop r2\n"
  32. "pop r1\n"
  33. );
  34. }
  35. // Write dataByte and return read value
  36. // Write 0x00 for a read
  37. // Writes byte over SPI0
  38. word spiflash_transfer(word dataByte)
  39. {
  40. word retval = 0;
  41. asm(
  42. "load32 0xC02728 r2 ; r2 = 0xC02728\n"
  43. "write 0 r2 r4 ; write r4 over SPI0\n"
  44. "read 0 r2 r2 ; read return value\n"
  45. "write -4 r14 r2 ; write to stack to return\n"
  46. );
  47. return retval;
  48. }
  49. // Initialize manual operation of SPI flash by enabling SPI0 and resetting the chip
  50. // This is needed to get out of continuous read mode and allow manual commands
  51. void spiflash_init()
  52. {
  53. // Already set CS high before enabling SPI0
  54. spiflash_end_transfer();
  55. // Enable SPI0 (connects simpleSPI.v and disconnects SPIreader.v)
  56. word *p = (word *) 0xC0272A; // Set address (SPI0 enable)
  57. *p = 1; // Write value
  58. delay(10);
  59. // Reset to get out of continuous read mode
  60. spiflash_begin_transfer();
  61. spiflash_transfer(0x66);
  62. spiflash_end_transfer();
  63. delay(1);
  64. spiflash_begin_transfer();
  65. spiflash_transfer(0x99);
  66. spiflash_end_transfer();
  67. delay(1);
  68. spiflash_begin_transfer();
  69. spiflash_transfer(0x66);
  70. spiflash_end_transfer();
  71. delay(1);
  72. spiflash_begin_transfer();
  73. spiflash_transfer(0x99);
  74. spiflash_end_transfer();
  75. delay(1);
  76. }
  77. // Reads manufacturer and device IDs and prints them over UART
  78. // Should print 239 as Winbond manufacturer, and 23 for W25Q128 device ID
  79. void spiflash_read_chip_ids()
  80. {
  81. spiflash_begin_transfer();
  82. spiflash_transfer(0x90);
  83. spiflash_transfer(0);
  84. spiflash_transfer(0);
  85. spiflash_transfer(0);
  86. word manufacturer = spiflash_transfer(0);
  87. word deviceID = spiflash_transfer(0);
  88. spiflash_end_transfer();
  89. uprint("Manufacturer ID: ");
  90. uprintDec(manufacturer);
  91. uprint("\n");
  92. uprint("Device ID: ");
  93. uprintDec(deviceID);
  94. uprint("\n");
  95. }
  96. // Enables write operation on the chip
  97. void spiflash_enable_write()
  98. {
  99. spiflash_begin_transfer();
  100. spiflash_transfer(0x06);
  101. spiflash_end_transfer();
  102. }
  103. // Reads len bytes from addr and stores them in output
  104. // If bytes_to_word is 1, then each 4 bytes are shifted into one word in output with len in words
  105. void spiflash_read_from_address(word* output, word addr, word len, word bytes_to_word)
  106. {
  107. spiflash_begin_transfer();
  108. spiflash_transfer(0x03);
  109. spiflash_transfer(addr >> 16);
  110. spiflash_transfer(addr >> 8);
  111. spiflash_transfer(addr);
  112. if (bytes_to_word)
  113. {
  114. len = len << 2;
  115. word i;
  116. for (i = 0; i < len; i++)
  117. {
  118. if ((i & 3) == 0)
  119. {
  120. output[i >> 2] = 0;
  121. }
  122. output[i >> 2] |= spiflash_transfer(0) << (24 - ((i & 3) << 3));
  123. }
  124. }
  125. else
  126. {
  127. word i;
  128. for (i = 0; i < len; i++)
  129. {
  130. output[i] = spiflash_transfer(0);
  131. }
  132. }
  133. spiflash_end_transfer();
  134. }
  135. // Writes up to 64 words as 256 bytes to the given address
  136. // Address must be aligned to a page boundary
  137. void spiflash_write_page_in_words(word* input, word addr, word len)
  138. {
  139. // Check if length exceeds page size
  140. if (len > 64)
  141. {
  142. uprintln("Error: cannot write more than a page!");
  143. return;
  144. }
  145. // Check if address is aligned to a page boundary
  146. if (addr & 0x3F)
  147. {
  148. uprintln("Error: address must be aligned to a page boundary!");
  149. return;
  150. }
  151. spiflash_enable_write();
  152. word status = spiflash_read_status_reg(1);
  153. // Check if write is enabled
  154. if ((status & 0x2) == 0)
  155. {
  156. uprintln("WE disabled!");
  157. return;
  158. }
  159. spiflash_begin_transfer();
  160. spiflash_transfer(0x02);
  161. spiflash_transfer(addr >> 16);
  162. spiflash_transfer(addr >> 8);
  163. spiflash_transfer(addr);
  164. word i;
  165. for (i = 0; i < len; i++)
  166. {
  167. spiflash_transfer(input[i] >> 24);
  168. spiflash_transfer(input[i] >> 16);
  169. spiflash_transfer(input[i] >> 8);
  170. spiflash_transfer(input[i]);
  171. }
  172. spiflash_end_transfer();
  173. // Wait for busy bit to be 0
  174. status = 1;
  175. while((status & 0x1) == 1)
  176. {
  177. status = spiflash_read_status_reg(1);
  178. }
  179. }
  180. // Reads status register 1, 2, or 3
  181. word spiflash_read_status_reg(word reg_idx)
  182. {
  183. if (reg_idx < 1 || reg_idx > 3)
  184. return 0;
  185. spiflash_begin_transfer();
  186. word command;
  187. switch (reg_idx)
  188. {
  189. case 1:
  190. command = 0x05;
  191. break;
  192. case 2:
  193. command = 0x35;
  194. break;
  195. case 3:
  196. command = 0x15;
  197. break;
  198. }
  199. spiflash_transfer(command);
  200. word status = spiflash_transfer(0);
  201. spiflash_end_transfer();
  202. return status;
  203. }
  204. // Executes sector erase operation
  205. // Erases the 4KiB sector of the given address
  206. // (e.g. addr 4096 erases the second sector)
  207. // This is the smallest unit that can be erased
  208. // Returns 1 on success
  209. word spiflash_sector_erase(word addr)
  210. {
  211. spiflash_enable_write();
  212. word status = spiflash_read_status_reg(1);
  213. // Check if write is enabled
  214. if ((status & 0x2) == 0)
  215. {
  216. uprintln("WE disabled!");
  217. return 0;
  218. }
  219. // Send command
  220. spiflash_begin_transfer();
  221. spiflash_transfer(0x20);
  222. spiflash_transfer(addr >> 16);
  223. spiflash_transfer(addr >> 8);
  224. spiflash_transfer(addr);
  225. spiflash_end_transfer();
  226. // Wait for busy bit to be 0
  227. status = 1;
  228. while((status & 0x1) == 1)
  229. {
  230. status = spiflash_read_status_reg(1);
  231. }
  232. return 1;
  233. }
  234. // Executes chip erase operation
  235. // TAKES AT LEAST 20 SECONDS!
  236. // Returns 1 on success
  237. word spiflash_chip_erase()
  238. {
  239. spiflash_enable_write();
  240. word status = spiflash_read_status_reg(1);
  241. // Check if write is enabled
  242. if ((status & 0x2) == 0)
  243. {
  244. uprintln("WE disabled!");
  245. return 0;
  246. }
  247. // Send erase chip command
  248. spiflash_begin_transfer();
  249. spiflash_transfer(0xC7);
  250. spiflash_end_transfer();
  251. // Wait for busy bit to be 0
  252. status = 1;
  253. while((status & 0x1) == 1)
  254. {
  255. delay(100);
  256. status = spiflash_read_status_reg(1);
  257. }
  258. return 1;
  259. }