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