eeprom.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. #include "catch.hpp"
  2. #include "EmuTime.hh"
  3. #include "EEPROM_93C46.hh"
  4. #include "XMLElement.hh"
  5. #include "xrange.hh"
  6. using namespace openmsx;
  7. constexpr auto step = EmuDuration::usec(10);
  8. static void input_pattern(EEPROM_93C46& eeprom, EmuTime& time, uint32_t pattern, unsigned len)
  9. {
  10. assert(len <= 32);
  11. for (auto i : xrange(len)) {
  12. eeprom.write_DI(pattern & (1 << (len - 1 - i)), time);
  13. time += step;
  14. eeprom.write_CLK(true, time);
  15. time += step;
  16. eeprom.write_CLK(false, time);
  17. time += step;
  18. }
  19. }
  20. static void write(EEPROM_93C46& eeprom, EmuTime& time, unsigned addr, uint8_t value)
  21. {
  22. assert(addr < EEPROM_93C46::NUM_ADDRESSES);
  23. eeprom.write_CS(true, time);
  24. time += step;
  25. uint32_t pattern = 0b101; // start-bit + write-opcode
  26. pattern <<= EEPROM_93C46::ADDRESS_BITS;
  27. pattern |= addr;
  28. pattern <<= EEPROM_93C46::DATA_BITS;
  29. pattern |= value;
  30. unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS + EEPROM_93C46::DATA_BITS;
  31. input_pattern(eeprom, time, pattern, len);
  32. eeprom.write_CS(false, time);
  33. time += step;
  34. }
  35. static void write_all(EEPROM_93C46& eeprom, EmuTime& time, uint8_t value)
  36. {
  37. eeprom.write_CS(true, time);
  38. time += step;
  39. uint32_t pattern = 0b100; // start-bit + write-opcode
  40. pattern <<= EEPROM_93C46::ADDRESS_BITS;
  41. pattern |= 0b0100000; // 0b01xxxxx
  42. pattern <<= EEPROM_93C46::DATA_BITS;
  43. pattern |= value;
  44. unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS + EEPROM_93C46::DATA_BITS;
  45. input_pattern(eeprom, time, pattern, len);
  46. eeprom.write_CS(false, time);
  47. time += step;
  48. }
  49. static void write_enable(EEPROM_93C46& eeprom, EmuTime& time)
  50. {
  51. eeprom.write_CS(true, time);
  52. time += step;
  53. uint32_t pattern = 0b100; // start-bit + '00'-opcode
  54. pattern <<= EEPROM_93C46::ADDRESS_BITS;
  55. pattern |= 0b1100000; // 0b11xxxxx
  56. unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
  57. input_pattern(eeprom, time, pattern, len);
  58. eeprom.write_CS(false, time);
  59. time += step;
  60. }
  61. static void write_disable(EEPROM_93C46& eeprom, EmuTime& time)
  62. {
  63. eeprom.write_CS(true, time);
  64. time += step;
  65. uint32_t pattern = 0b100; // start-bit + '00'-opcode
  66. pattern <<= EEPROM_93C46::ADDRESS_BITS;
  67. pattern |= 0b0000000; // 0b00xxxxx
  68. unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
  69. input_pattern(eeprom, time, pattern, len);
  70. eeprom.write_CS(false, time);
  71. time += step;
  72. }
  73. static bool waitIdle(EEPROM_93C46& eeprom, EmuTime& time)
  74. {
  75. eeprom.write_CS(true, time);
  76. time += step;
  77. bool wasBusy = false;
  78. int i = 0;
  79. while (i < 10'000) {
  80. bool ready = eeprom.read_DO(time);
  81. time += step;
  82. if (ready) break;
  83. wasBusy = true;
  84. }
  85. CHECK(i < 10'000); // we must not wait indefinitely
  86. eeprom.write_CS(false, time);
  87. time += step;
  88. return wasBusy;
  89. }
  90. static uint8_t read(EEPROM_93C46& eeprom, EmuTime& time, unsigned addr)
  91. {
  92. assert(addr < EEPROM_93C46::NUM_ADDRESSES);
  93. eeprom.write_CS(true, time);
  94. time += step;
  95. uint32_t pattern = 0b110; // start-bit + write-opcode
  96. pattern <<= EEPROM_93C46::ADDRESS_BITS;
  97. pattern |= addr;
  98. unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
  99. input_pattern(eeprom, time, pattern, len);
  100. CHECK(eeprom.read_DO(time) == false); // initial 0-bit
  101. uint8_t result = 0;
  102. for (auto i : xrange(EEPROM_93C46::DATA_BITS)) {
  103. (void)i;
  104. eeprom.write_CLK(true, time);
  105. time += step;
  106. result <<= 1;
  107. result |= eeprom.read_DO(time);
  108. time += step;
  109. eeprom.write_CLK(false, time);
  110. time += step;
  111. }
  112. eeprom.write_CS(false, time);
  113. time += step;
  114. return result;
  115. }
  116. static void read_block(EEPROM_93C46& eeprom, EmuTime& time, unsigned addr,
  117. unsigned num, uint8_t* output)
  118. {
  119. assert(addr < EEPROM_93C46::NUM_ADDRESSES);
  120. eeprom.write_CS(true, time);
  121. time += step;
  122. uint32_t pattern = 0b110; // start-bit + write-opcode
  123. pattern <<= EEPROM_93C46::ADDRESS_BITS;
  124. pattern |= addr;
  125. unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
  126. input_pattern(eeprom, time, pattern, len);
  127. CHECK(eeprom.read_DO(time) == false); // initial 0-bit
  128. for (auto j : xrange(num)) {
  129. (void)j;
  130. *output = 0;
  131. for (auto i : xrange(EEPROM_93C46::DATA_BITS)) {
  132. (void)i;
  133. eeprom.write_CLK(true, time);
  134. time += step;
  135. *output <<= 1;
  136. *output |= eeprom.read_DO(time);
  137. time += step;
  138. eeprom.write_CLK(false, time);
  139. time += step;
  140. }
  141. ++output;
  142. }
  143. eeprom.write_CS(false, time);
  144. time += step;
  145. }
  146. static void erase(EEPROM_93C46& eeprom, EmuTime& time, unsigned addr)
  147. {
  148. assert(addr < EEPROM_93C46::NUM_ADDRESSES);
  149. eeprom.write_CS(true, time);
  150. time += step;
  151. uint32_t pattern = 0b111; // start-bit + '00'-opcode
  152. pattern <<= EEPROM_93C46::ADDRESS_BITS;
  153. pattern |= addr;
  154. unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
  155. input_pattern(eeprom, time, pattern, len);
  156. eeprom.write_CS(false, time);
  157. time += step;
  158. }
  159. static void erase_all(EEPROM_93C46& eeprom, EmuTime& time)
  160. {
  161. eeprom.write_CS(true, time);
  162. time += step;
  163. uint32_t pattern = 0b100; // start-bit + '00'-opcode
  164. pattern <<= EEPROM_93C46::ADDRESS_BITS;
  165. pattern |= 0b1000000; // 0b10xxxxx
  166. unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
  167. input_pattern(eeprom, time, pattern, len);
  168. eeprom.write_CS(false, time);
  169. time += step;
  170. }
  171. TEST_CASE("EEPROM_93C46")
  172. {
  173. XMLElement xml;
  174. EEPROM_93C46 eeprom(xml);
  175. const uint8_t* data = eeprom.backdoor();
  176. EmuTime time = EmuTime::zero();
  177. // initially filled with 255
  178. for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
  179. CHECK(data[addr] == 255);
  180. }
  181. // write 123 to address 45 .. but eeprom is still write-protected
  182. write(eeprom, time, 45, 123);
  183. CHECK(!waitIdle(eeprom, time));
  184. for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
  185. CHECK(data[addr] == 255);
  186. }
  187. // again, but first write-enable
  188. write_enable(eeprom, time);
  189. CHECK(!waitIdle(eeprom, time));
  190. write(eeprom, time, 45, 123);
  191. CHECK(waitIdle(eeprom, time));
  192. for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
  193. uint8_t expected = (addr == 45) ? 123 : 255;
  194. CHECK(data[addr] == expected);
  195. }
  196. // read address 45
  197. CHECK(int(read(eeprom, time, 45)) == 123); // contains 123
  198. CHECK(!waitIdle(eeprom, time)); // not busy after read
  199. CHECK(int(read(eeprom, time, 46)) == 255); // other addr still contains 255
  200. CHECK(!waitIdle(eeprom, time)); // not busy after read
  201. // write to address 45 without first erasing it
  202. // You might expect to read the value 123 & 20 == 16, but the M93C46
  203. // datasheet documents that the write command does an auto-erase.
  204. write(eeprom, time, 45, 20);
  205. CHECK(waitIdle(eeprom, time));
  206. for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
  207. uint8_t expected = (addr == 45) ? 20 : 255;
  208. CHECK(data[addr] == expected);
  209. }
  210. // erase addr 99 -> doesn't influence addr 45
  211. erase(eeprom, time, 99);
  212. CHECK(waitIdle(eeprom, time));
  213. for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
  214. uint8_t expected = (addr == 45) ? 20 : 255;
  215. CHECK(data[addr] == expected);
  216. }
  217. // erase addr 45 -> all 255 again
  218. erase(eeprom, time, 45);
  219. CHECK(waitIdle(eeprom, time));
  220. for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
  221. CHECK(data[addr] == 255);
  222. }
  223. // write all
  224. write_all(eeprom, time, 77);
  225. CHECK(waitIdle(eeprom, time));
  226. for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
  227. CHECK(data[addr] == 77);
  228. }
  229. // write to the end and the start of the address space
  230. write(eeprom, time, EEPROM_93C46::NUM_ADDRESSES - 2, 5);
  231. CHECK(waitIdle(eeprom, time));
  232. write(eeprom, time, EEPROM_93C46::NUM_ADDRESSES - 1, 11);
  233. CHECK(waitIdle(eeprom, time));
  234. write(eeprom, time, 0, 22);
  235. CHECK(waitIdle(eeprom, time));
  236. // write-disable and one more write
  237. write_disable(eeprom, time);
  238. CHECK(!waitIdle(eeprom, time));
  239. write(eeprom, time, 1, 33); // not executed
  240. CHECK(!waitIdle(eeprom, time));
  241. for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
  242. uint8_t expected = (addr == 126) ? 5
  243. : (addr == 127) ? 11
  244. : (addr == 0) ? 22
  245. : 77;
  246. CHECK(data[addr] == expected);
  247. }
  248. // read-block, with wrap-around
  249. uint8_t buf[6];
  250. read_block(eeprom, time, EEPROM_93C46::NUM_ADDRESSES - 3, 6, buf);
  251. CHECK(!waitIdle(eeprom, time));
  252. CHECK(buf[0] == 77);
  253. CHECK(buf[1] == 5);
  254. CHECK(buf[2] == 11);
  255. CHECK(buf[3] == 22); // wrapped to address 0
  256. CHECK(buf[4] == 77); // was write protected
  257. CHECK(buf[5] == 77);
  258. // erase-all, but write-protected
  259. erase_all(eeprom, time);
  260. CHECK(!waitIdle(eeprom, time));
  261. CHECK(data[0] == 22); // not changed
  262. // erase-all, write-enabled
  263. write_enable(eeprom, time);
  264. CHECK(!waitIdle(eeprom, time));
  265. erase_all(eeprom, time);
  266. CHECK(waitIdle(eeprom, time));
  267. for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
  268. CHECK(data[addr] == 255);
  269. }
  270. }