mrfioc2  2.3.0
flashtest.cpp
Go to the documentation of this file.
1 #include <vector>
2 #include <algorithm>
3 #include <stdexcept>
4 #include <sstream>
5 
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include <epicsMath.h>
10 #include "epicsUnitTest.h"
11 #include "epicsString.h"
12 #include "testMain.h"
13 
14 #include "mrf/spi.h"
15 #include "mrf/flash.h"
16 
17 namespace {
18 
19 void testTimeout()
20 {
21  testDiag("testTimeout()");
22 
23  mrf::TimeoutCalculator C(1.0, 2.0, 0.1);
24 
25  testOk1(C.ok());
26  testOk1(C.inc()==0.0);
27  testOk1(C.sofar()==0.0);
28 
29  testOk1(C.ok());
30  testOk1(C.inc()==0.1);
31  testOk(fabs(C.sofar()-0.1)<0.001, "%f == 0.1", C.sofar());
32 
33  testOk1(C.ok());
34  testOk1(C.inc()==0.2);
35  testOk(fabs(C.sofar()-0.3)<0.001, "%f == 0.3", C.sofar());
36 
37  testOk1(C.ok());
38  testOk1(C.inc()==0.4);
39  testOk(fabs(C.sofar()-0.7)<0.001, "%f == 0.7", C.sofar());
40 
41  testOk1(C.ok());
42  testOk1(C.inc()==0.8);
43  testOk(fabs(C.sofar()-1.5)<0.001, "%f == 1.5", C.sofar());
44 
45  testOk1(!C.ok());
46 }
47 
49 struct TestDevice : public mrf::SPIInterface
50 {
51  enum state_t {
52  Idle,
53  Addr,
54  Wait,
55  Data,
56  Error,
57  } current;
58  unsigned command;
59  unsigned count;
60  epicsUInt32 address;
61  epicsUInt8 status;
62  bool selected;
63  bool WE;
64 
65  std::vector<epicsUInt8> ram;
66 
67  TestDevice() :current(Idle), command(0), count(0), address(0), status(0), selected(false), WE(false) {}
68 
69  virtual void select(unsigned id)
70  {
71  testDiag("select %u", id);
72 
73  bool select = !!id;
74 
75  if(!select) {
76  // some commands (eg. erase) clear WE on completion.
77  // as there is no functional issue with sending ENABLE WRITE
78  // too often, we pesemistically require this after all erase/program
79  switch(command) {
80  case 0xd8: // sector erase
81  case 0x02: // page program
82  WE = false;
83  }
84 
85  current = Idle;
86  command = count = 0;
87  address = 0;
88  }
89 
90  selected = select;
91  }
92 
93  virtual epicsUInt8 cycle(epicsUInt8 in)
94  {
95  //testDiag("cycle(%02x) current=%u command=%02x count=%u", (unsigned)in, current, command, count);
96 
97  if(!selected) throw std::logic_error("cycle() when not selected");
98 
99  if((status&1) && current==Idle && in!=0x05)
100  throw std::logic_error("Only READ STATUS allowed while busy");
101 
102  switch(current) {
103  case Idle:
104  command = in;
105  switch(in) {
106  case 0x04: // write disable
107  testDiag("Write Disable");
108  WE = false;
109  current = Error;
110  break;
111  case 0x06: // write enable
112  testDiag("Write Enable");
113  WE = true;
114  status |= 2;
115  current = Error;
116  break;
117  case 0xd8: // sector erase
118  case 0x02: // page program
119  if(!WE)
120  throw std::logic_error("Can't erase/program when !WE");
121  case 0x0b: // fast read
122  count = 0;
123  current = Addr;
124  break;
125  case 0x05: // read status
126  case 0x9f: // read ID
127  count = 0;
128  current = Data;
129  break;
130  default:
131  throw std::logic_error("Unknown command");
132  }
133  break;
134  case Addr:
135  switch(count++) {
136  case 0: address = epicsUInt32(in)<<16; break;
137  case 1: address |= epicsUInt32(in)<<8; break;
138  case 2:
139  address |= epicsUInt32(in)<<0;
140  switch(command) {
141  case 0x0b:
142  current = Wait;
143  break;
144  case 0xd8:
145  testDiag("Erase sector %06x", (unsigned)address);
146  status |= 1;
147  for(epicsUInt32 cur=address, end=address+64*1024u; cur<end && cur<ram.size(); cur++)
148  ram[cur] = 0xff;
149  current = Error;
150  break;
151  case 0x02:
152  testDiag("program page %06x", (unsigned)address);
153  count = 0;
154  default:
155  current = Data;
156  break;
157  }
158  }
159  break;
160  case Wait:
161  current = Data;
162  break;
163  case Data:
164  if(command==0x02) {
165  if(count++>=256)
166  throw std::logic_error("Can't program more than one page");
167 
168  if(address>=ram.size())
169  ram.resize(address+1, 0xff);
170 
171  if(ram[address]!=0xff)
172  throw std::logic_error("Write w/ erase");
173 
174  ram[address] = in;
175  address++;
176 
177  } else if(command==0x05) {
178  epicsUInt8 ret = status;
179  status &= ~1;
180  testDiag("status -> %02x", ret);
181  return ret;
182 
183  } else if(command==0x0b) {
184  count++;
185  if(address<ram.size())
186  return ram[address++];
187 
188  } else if(command==0x9f) {
189  switch(++count) {
190  case 1: return 0x20;
191  case 2: return 0xba;
192  case 3: return 0x17; // 64Mbit (8MB)
193  case 4: return 0x10; // 16 bytes payload
194  case 5: return 0x00;
195  case 6: return 0x00;
196  // arbitrary UID
197  case 7: return 0x01;
198  case 8: return 0x02;
199  case 9: return 0x03;
200  case 10: return 0x04;
201  case 11: return 0x05;
202  case 12: return 0x06;
203  case 13: return 0x07;
204  case 14: return 0x08;
205  case 15: return 0x09;
206  case 16: return 0x0a;
207  case 17: return 0x0b;
208  case 18: return 0x0c;
209  case 19: return 0x0d;
210  case 20: return 0x0e;
211  }
212  }
213  break;
214  case Error:
215  throw std::logic_error("Cycle during bad state");
216  break;
217  }
218  return 0xff;
219  }
220 };
221 
222 void testReadID()
223 {
224  testDiag("testReadID()");
225 
226  TestDevice iface;
227  mrf::SPIDevice dev(&iface, 1);
228  mrf::CFIFlash mem(dev);
229 
230  mrf::CFIFlash::ID info;
231 
232  mem.readID(&info);
233 
234  testOk1(info.vendor==0x20);
235  testOk1(info.dev_type==0xba);
236  testOk1(info.dev_id==0x17);
237  // capacity >16MB is truncated to 16MB (we use 24-bit operations)
238  testOk1(info.capacity==8*1024*1024);
239  testOk1(info.pageSize==256);
240  testOk1(info.sectorSize==64*1024);
241 }
242 
243 void testRead()
244 {
245  testDiag("testRead");
246 
247  TestDevice iface;
248 
249  iface.ram.resize(128);
250  for(size_t i=0; i<iface.ram.size(); i++)
251  iface.ram[i] = i|0x80;
252 
253  mrf::SPIDevice dev(&iface, 1);
254  mrf::CFIFlash mem(dev);
255 
256  {
257  testDiag("Read one");
258  epicsUInt8 val;
259 
260  mem.read(0, 1, &val);
261 
262  testOk1(val==0x80);
263 
264  mem.read(0x23, 1, &val);
265 
266  testOk1(val==0xa3);
267 
268  mem.read(0x256, 1, &val);
269 
270  testOk1(val==0xff);
271  }
272 
273  {
274  testDiag("Read many");
275  epicsUInt8 val[8];
276 
277  mem.read(0, 8, val);
278  testOk1(val[0]==0x80);
279  testOk1(val[1]==0x81);
280  testOk1(val[2]==0x82);
281  testOk1(val[3]==0x83);
282  testOk1(val[4]==0x84);
283  testOk1(val[5]==0x85);
284  testOk1(val[6]==0x86);
285  testOk1(val[7]==0x87);
286 
287  mem.read(16, 8, val);
288  testOk1(val[0]==0x90);
289  testOk1(val[1]==0x91);
290  testOk1(val[2]==0x92);
291  testOk1(val[3]==0x93);
292  testOk1(val[4]==0x94);
293  testOk1(val[5]==0x95);
294  testOk1(val[6]==0x96);
295  testOk1(val[7]==0x97);
296  }
297 }
298 
299 void testWrite()
300 {
301  static const epicsUInt8 helloworld[] = "helloworld";
302  static const epicsUInt8 thisisatest[] = "thisisatest";
303  testDiag("testWrite()");
304 
305  TestDevice iface;
306  mrf::SPIDevice dev(&iface, 1);
307  mrf::CFIFlash mem(dev);
308 
309  testDiag("Write one page");
310 
311  mem.write(0, 10, helloworld, false);
312 
313  testOk(iface.ram.size()==10, "%zu == 10", iface.ram.size());
314 
315  testOk1(memcmp(helloworld, &iface.ram[0], 10)==0);
316 
317  testDiag("Write one page");
318 
319  mem.write(2*64*1024, 11, thisisatest, false);
320 
321  testOk(iface.ram.size()==2*64*1024 + 11, "%zu == 64kb + 11", iface.ram.size());
322 
323  testOk1(memcmp(helloworld, &iface.ram[0], 10)==0);
324  testOk1(iface.ram[10]==0xff);
325  testOk1(iface.ram[2*64*1024 - 1]==0xff);
326  testOk1(memcmp(thisisatest, &iface.ram[2*64*1024], 11)==0);
327 
328  testDiag("Write many pages");
329 
330  std::vector<epicsUInt8> bigbuf(6*64*1024);
331  for(epicsUInt32 i=0, cnt=0; i<bigbuf.size(); i+=4, cnt++)
332  {
333  bigbuf[i+0] = cnt>>24;
334  bigbuf[i+1] = cnt>>16;
335  bigbuf[i+2] = cnt>> 8;
336  bigbuf[i+3] = cnt>> 0;
337  }
338 
339  mem.write(0, bigbuf, true);
340 
341  testOk(iface.ram.size()==bigbuf.size(), "%zu == %zu", iface.ram.size(), bigbuf.size());
342 
343  bool match=true;
344  for(size_t i=0, N=std::min(iface.ram.size(), bigbuf.size()); i<N; i++)
345  {
346  bool ok = iface.ram[i]==bigbuf[i];
347  match &= ok;
348  }
349  testOk1(!!match);
350 }
351 
352 static const char xiheader[] = "\x00\t\x0f\xf0\x0f\xf0\x0f\xf0\x0f\xf0\x00\x00\x01""a\x00/"
353  "mtcaeevm300;User"
354  "ID=0XFFFFFFFF;Ve"
355  "rsion=2017.4.1\x00""b"
356  "\x00\r7k325tfbg900\x00""c"
357  "\x00\x0b""2018/07/23\x00""d\x00\t"
358  "08:57:10\x00""e\x00\xae\x9d\x9c\xff\xff";
359 
360 void testStream()
361 {
362  testDiag("testStream()");
363 
364  TestDevice iface;
365  mrf::SPIDevice dev(&iface, 1);
366  mrf::CFIFlash mem(dev);
367 
368  mem.write(0, sizeof(xiheader)-1, (const epicsUInt8*)xiheader, false);
369 
370  mrf::CFIStreamBuf sbuf(mem);
371  std::istream strm(&sbuf);
372 
373  std::vector<char> cbuf(13);
374 
375  testOk(strm.read(&cbuf[0], cbuf.size()).gcount()==std::streamsize(cbuf.size()),
376  "read %u == %u", unsigned(strm.gcount()), unsigned(cbuf.size()));
377  testOk1(!!strm.good());
378  testOk1(!!std::equal(cbuf.begin(), cbuf.end(), xiheader));
379 
380  // retry
381  testDiag("seek(0)");
382  strm.seekg(0);
383  testOk1(!!strm.good());
384  testOk(strm.tellg()==0, "tell -> %d", int(strm.tellg()));
385  sbuf.pubseekpos(0);
386 
387  std::fill(cbuf.begin(), cbuf.end(), 0);
388 
389  testOk(strm.read(&cbuf[0], 6).gcount()==6,
390  "read %u == %u", unsigned(strm.gcount()), 6);
391  testOk1(!!strm.good());
392  testOk(strm.read(&cbuf[6], 7).gcount()==7,
393  "read %u == %u", unsigned(strm.gcount()), 7);
394  testOk1(!!strm.good());
395  testOk1(!!std::equal(cbuf.begin(), cbuf.end(), xiheader));
396 }
397 
398 void testXilinx()
399 {
400  testDiag("testXilinx()");
401 
402  std::istringstream strm(std::string(xiheader, sizeof(xiheader)-1));
403 
404  mrf::XilinxBitInfo info;
405  testOk1(!!info.read(strm));
406  testOk(info.project=="mtcaeevm300;UserID=0XFFFFFFFF;Version=2017.4.1",
407  "project='%s'", info.project.c_str());
408  testOk(info.part=="7k325tfbg900",
409  "part='%s'", info.part.c_str());
410  testOk(info.date=="2018/07/23 08:57:10",
411  "date='%s'", info.date.c_str());
412 }
413 
414 } // namespace
415 
416 MAIN(flashTest)
417 {
418  testPlan(64);
419  try{
420  testTimeout();
421  testReadID();
422  testRead();
423  testWrite();
424  testStream();
425  testXilinx();
426  }catch(std::exception& e){
427  testAbort("Exception %s", e.what());
428  }
429  return testDone();
430 }
epicsUInt32 pageSize
PAGE PROGRAM (0x02) size in bytes. Always a power of 2.
Definition: flash.h:38
virtual epicsUInt8 cycle(epicsUInt8 in)=0
std::string date
Definition: flash.h:110
epicsUInt32 capacity
total capacity in bytes
Definition: flash.h:38
Attempt to read out the header of a Xilinx bitstream file.
Definition: flash.h:104
Handling for Common Flash Interfafce compliant chips.
Definition: flash.h:23
epicsUInt32 sectorSize
SECTOR ERASE (0xd8) size in bytes. Always a power of 2.
Definition: flash.h:38
std::string part
Definition: flash.h:110
epicsUInt8 dev_type
Definition: flash.h:33
std::string project
Definition: flash.h:110
MAIN(flashTest)
Definition: flashtest.cpp:416
epicsUInt8 vendor
Definition: flash.h:33
bool read(std::istream &strm)
Definition: flash.cpp:405
virtual void select(unsigned id)=0
Select numbered device. 0 clears selection.
Definition: spi.cpp:21
Interface for SPI Master.
Definition: spi.h:20
Adapt CFIFlash for use with std::istream.
Definition: flash.h:89
epicsUInt8 dev_id
Definition: flash.h:33