mrfioc2  2.3.0
flash.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2017 Michael Davidsaver
3 * mrfioc2 is distributed subject to a Software License Agreement found
4 * in file LICENSE that is included with this distribution.
5 \*************************************************************************/
6 
7 #include <stdexcept>
8 #include <vector>
9 
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <errno.h>
13 #include <string.h>
14 
15 #include <epicsStdio.h>
16 #include <epicsThread.h>
17 #include <osiSock.h>
18 
19 #include "mrfCommon.h"
20 #include "mrf/spi.h"
21 #include "mrf/flash.h"
22 
23 namespace mrf {
24 
26  :dev(dev)
27  ,haveinfo(false)
28 {}
29 
31 
32 void
34 {
35  epicsUInt8 cmd[7] = {0x9f, 0u, 0u, 0u},
36  response[7];
37  SPIInterface::Operation ops[2] = {{4, cmd, response}};
38  ops[0].ncycles = 4;
39  ops[0].in = cmd;
40  ops[0].out = response;
41 
42  {
43  SPIDevice::Selector S(dev);
44  dev.interface()->cycles(1, ops);
45  }
46 
47  if(response[1]==0xff)
48  {
49  /* the first read after power up seems to fail.
50  * Presumably some missing initializtion on my part.
51  * A single re-try.
52  */
53  SPIDevice::Selector S(dev);
54  dev.interface()->cycles(1, ops);
55  }
56 
57  id->vendor = response[1];
58  id->dev_type = response[2];
59  id->dev_id = response[3];
60 
61  id->vendorName = "<Unknown>";
62  id->capacity = id->sectorSize = id->pageSize = 0u;
63 
64  if(id->vendor==0x20) { // Micron
65  /* Some Micron parts support the Serial Flash Discoverable Parameter (SFDP) spec.
66  * using command 0x5a (see tech note TN-25-06).
67  * However, so far there isn't anything so interesting.
68  */
69  id->vendorName = "Micron";
70 
71  switch(id->dev_type) {
72  case 0x20: // m25p (Numonyx)
73  id->capacity = 1u<<(id->dev_id);
74  id->sectorSize = 256*1024u; // 2Mbit
75  id->pageSize = 256;
76  break;
77  case 0xba: // mt25q
78  case 0xbb:
79  id->capacity = 1u<<(id->dev_id);
80  id->sectorSize = 64*1024u;
81  id->pageSize = 256;
82  break;
83  }
84 
85  /* Micron puts some extra info after the regular 0x9f payload,
86  * including a serial number.
87  * Byte 4 is the length of the additional payload
88  */
89  ops[0].ncycles = 5;
90 
91  {
92  SPIDevice::Selector S(dev);
93  dev.interface()->cycles(1, ops);
94  }
95 
96  if(response[4]>2) {
97  ops[0].ncycles = 7;
98 
99  id->SN.resize(response[4]-2);
100  ops[1].ncycles = id->SN.size();
101  ops[1].in = NULL;
102  ops[1].out = &id->SN[0];
103 
104  SPIDevice::Selector S(dev);
105  dev.interface()->cycles(2, ops);
106 
107  // if this byte isn't 0, then the spec isn't being followed
108  if(response[6]!=0)
109  id->SN.clear();
110 
111  if((response[5]&0x3)!=0) {
112  // sector size not known
113  id->sectorSize = 0;
114  }
115  }
116  }
117 
118  // we only use 24-bit read/write/erase ops
119  // so capacity beyond 16MB is not accessible.
120  if(id->capacity>0x1000000)
121  id->capacity = 0x1000000;
122 }
123 
124 void CFIFlash::read(epicsUInt32 start, epicsUInt32 count, epicsUInt8 *data)
125 {
126  if((start&0xff000000) || (count&0xff000000) || ((start+count)&0xff000000))
127  std::runtime_error("start/count exceeds 24-bit addressing");
128 
129  check();
130 
131  epicsUInt8 cmd[5];
132 
133  // as we don't control clock speed, assume support for "fast" read
134  // w/ dummy byte (wait state) after 24-bit address
135  cmd[0] = 0x0b;
136  cmd[1] = (start>>16)&0xff;
137  cmd[2] = (start>> 8)&0xff;
138  cmd[3] = (start>> 0)&0xff;
139  cmd[4] = 0; // dummy
140 
142 
143  ops[0].ncycles = 5;
144  ops[0].out = NULL;
145  ops[0].in = cmd;
146 
147  ops[1].ncycles = count;
148  ops[1].out = data;
149  ops[1].in = NULL;
150 
151  {
152  SPIDevice::Selector S(dev);
153  dev.interface()->cycles(2, ops);
154  }
155 }
156 
157 void CFIFlash::write(const epicsUInt32 start,
158  const epicsUInt32 count,
159  const epicsUInt8 * const data,
160  const bool strict)
161 {
162  if((start&0xff000000) || (count&0xff000000) || ((start+count)&0xff000000))
163  std::runtime_error("start/count exceeds 24-bit addressing");
164 
165  check();
166 
167  if(strict && info.capacity==0)
168  throw std::runtime_error("Won't attempt to write when capacity isn't known");
169 
170  else if(start>=info.capacity || (start+count)>info.capacity)
171  throw std::runtime_error("Can't write beyond capacity");
172 
173  if(info.pageSize==0 || info.sectorSize==0)
174  throw std::runtime_error("Won't attempt to write to unsupported flash chip");
175 
176  {
177  epicsUInt32 mask = (info.pageSize-1u) | (info.sectorSize-1u);
178  if(start&mask)
179  throw std::runtime_error("start address not aligned to page & sector sizes");
180 
181  if(strict && ((start+count)&mask))
182  throw std::runtime_error("end address not aligned to page & sector sizes");
183  }
184 
185  const epicsUInt32 end = start+count;
186 
187  const double timeout = dev.interface()->timeout();
188 
189  {
190  WriteEnabler WE(*this);
191 
192  // erase necessary sectors
193 
194  for(epicsUInt32 addr=start; addr<end; addr+=info.sectorSize)
195  {
196  busyWait(timeout);
197 
198  WE.enable();
199 
200  epicsUInt8 cmd[4];
201  cmd[0] = 0xd8; // SECTOR ERASE
202  cmd[1] = (addr>>16)&0xff;
203  cmd[2] = (addr>> 8)&0xff;
204  cmd[3] = (addr>> 0)&0xff;
205  SPIInterface::Operation op = {4, cmd, NULL};
206 
207  {
208  SPIDevice::Selector S(dev);
209  dev.interface()->cycles(1, &op);
210  }
211  }
212 
213  // program all pages
214 
215  const epicsUInt8 *cur = data;
216  for(epicsUInt32 addr=start; addr<end; addr+=info.pageSize, cur+=info.pageSize)
217  {
218  busyWait(timeout);
219 
220  WE.enable();
221 
222  const epicsUInt32 N = std::min(info.pageSize, end-addr);
223 
224  epicsUInt8 cmd[4];
225  cmd[0] = 0x02; // PAGE PROGRAM
226  cmd[1] = (addr>>16)&0xff;
227  cmd[2] = (addr>> 8)&0xff;
228  cmd[3] = (addr>> 0)&0xff;
229 
231  ops[0].ncycles = 4;
232  ops[0].in = cmd;
233  ops[0].out = NULL;
234 
235  ops[1].ncycles = N;
236  ops[1].in = cur;
237  ops[1].out = NULL;
238 
239  {
240  SPIDevice::Selector S(dev);
241  dev.interface()->cycles(2, ops);
242  }
243  }
244 
245  busyWait(timeout);
246 
247  // end of write phase
248  }
249 
250  // readback to verify by sector
251 
252  std::vector<epicsUInt8> scratch;
253 
254  const epicsUInt8 *cur = data;
255  bool ok = true;
256  for(epicsUInt32 addr=start; addr<end; addr+=info.sectorSize, cur+=info.sectorSize)
257  {
258  const epicsUInt32 N = std::min(info.sectorSize, end-addr);
259 
260  scratch.resize(N);
261 
262  read(addr, scratch);
263 
264  if(memcmp(cur, &scratch[0], N)!=0) {
265  printf("FLASH readback mis-match in sector 0x%06x\n", (unsigned)addr);
266  ok = false;
267  }
268  }
269 
270  if(!ok)
271  throw std::runtime_error("FLASH readback error");
272 }
273 
274 void CFIFlash::erase(epicsUInt32 start, epicsUInt32 count, bool strict)
275 {
276  if((start&0xff000000) || (count&0xff000000) || ((start+count)&0xff000000))
277  std::runtime_error("start/count exceeds 24-bit addressing");
278 
279  check();
280 
281  if(strict && info.capacity==0)
282  throw std::runtime_error("Won't attempt to write when capacity isn't known");
283 
284  else if(start>=info.capacity || (start+count)>info.capacity)
285  throw std::runtime_error("Can't write beyond capacity");
286 
287  if(info.pageSize==0 || info.sectorSize==0)
288  throw std::runtime_error("Won't attempt to write to unsupported flash chip");
289 
290  {
291  epicsUInt32 mask = (info.pageSize-1u) | (info.sectorSize-1u);
292  if(start&mask)
293  throw std::runtime_error("start address not aligned to page & sector sizes");
294 
295  if(strict && ((start+count)&mask))
296  throw std::runtime_error("end address not aligned to page & sector sizes");
297  }
298 
299  const epicsUInt32 end = start+count;
300 
301  const double timeout = dev.interface()->timeout();
302 
303  WriteEnabler WE(*this);
304 
305  // erase necessary sectors
306 
307  for(epicsUInt32 addr=start; addr<end; addr+=info.sectorSize)
308  {
309  busyWait(timeout);
310 
311  WE.enable();
312 
313  epicsUInt8 cmd[4];
314  cmd[0] = 0xd8; // SECTOR ERASE
315  cmd[1] = (addr>>16)&0xff;
316  cmd[2] = (addr>> 8)&0xff;
317  cmd[3] = (addr>> 0)&0xff;
318  SPIInterface::Operation op = {4, cmd, NULL};
319 
320  {
321  SPIDevice::Selector S(dev);
322  dev.interface()->cycles(1, &op);
323  }
324  }
325 }
326 
327 void CFIFlash::check()
328 {
329  if(!haveinfo) {
330 
331  readID(&info);
332 
333  haveinfo = true;
334  }
335 
336  if(info.vendor==0xff)
337  throw std::runtime_error("Invalid Flash vendor ID");
338 }
339 
340 unsigned CFIFlash::status()
341 {
342  epicsUInt8 cmd[2] = {0x05, 0}, response[2] = {0,0};
343  SPIInterface::Operation op = {2, cmd, response};
344 
345  SPIDevice::Selector S(dev);
346  dev.interface()->cycles(1, &op);
347 
348  return response[1]&0x03; // pass out only 0x01 busy and 0x02 write enabled
349 }
350 
351 void CFIFlash::writeEnable(bool e)
352 {
353  epicsUInt8 cmd = e ? 0x06 : 0x04;
354  SPIInterface::Operation op = {1, &cmd, NULL};
355 
356  SPIDevice::Selector S(dev);
357  dev.interface()->cycles(1, &op);
358 }
359 
360 void CFIFlash::busyWait(double timeout, unsigned n)
361 {
362  TimeoutCalculator T(timeout);
363 
364  while(T.ok() && (status()&1))
365  epicsThreadSleep(T.inc());
366 
367  if(!T.ok())
368  throw std::runtime_error("Timeout waiting for operation to complete");
369 }
370 
372  :flash(flash)
373  ,pos(0u)
374 {}
375 
376 CFIStreamBuf::int_type CFIStreamBuf::underflow()
377 {
378  // read-ahead is only one page
379  buf.resize(flash.pageSize());
380  flash.read(pos, buf.size(), (epicsUInt8*)&buf[0]);
381  setg(&buf[0], &buf[0], &buf[buf.size()]);
382  pos += buf.size();
383 
384  return buf[0];
385 }
386 
387 CFIStreamBuf::pos_type
388 CFIStreamBuf::seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode mode)
389 {
390  if(dir==std::ios_base::cur)
391  off += pos;
392  else if(dir==std::ios_base::end)
393  return pos_type(-1); // error
394  return seekpos(off, mode);
395 }
396 
397 CFIStreamBuf::pos_type
398 CFIStreamBuf::seekpos(pos_type off, std::ios_base::openmode mode)
399 {
400  pos = off;
401  setg(&buf[0], &buf[0], &buf[0]); // empty buffer
402  return pos;
403 }
404 
405 bool XilinxBitInfo::read(std::istream& strm)
406 {
407  static const char header[] = "\0\x09\x0f\xf0\x0f\xf0\x0f\xf0\x0f\xf0\0\0\1";
408 
409  std::vector<char> buf(sizeof(header)-1);
410 
411  if(strm.read(&buf[0], buf.size()).gcount()!=std::streamsize(buf.size()) || !std::equal(buf.begin(), buf.end(), header))
412  return false; // not a .bit file
413 
414  std::string project, part, date, time;
415 
416  bool eof = false;
417  while(!eof) {
418  epicsUInt8 id;
419  epicsUInt16 size;
420 
421  if(strm.read((char*)&id, 1).gcount()==0)
422  break;
423 
424  switch(id) {
425  case 0x61:
426  case 0x62:
427  case 0x63:
428  case 0x64:
429  if(strm.read((char*)&size, 2).gcount()!=2)
430  throw std::runtime_error("Truncated block header");
431  size = ntohs(size);
432  buf.resize(size);
433  if(strm.read(&buf[0], buf.size()).gcount()!=std::streamsize(buf.size()))
434  throw std::runtime_error("Truncated block payload");
435 
436  // there are all actually printable strings, so ensure they have a trailing nil.
437  buf.push_back('\0');
438 
439  break;
440  case 0x65:
441  // start of bit stream content, we treat this as end of "header" and stop here.
442  // in practice there is nothing after the bit stream anyway.
443  eof = true;
444  break;
445  default:
446  fprintf(stderr, "Warning: attempting to ignore unknown block 0x%02x\n", unsigned(id));
447  eof = true;
448  break;
449  }
450 
451  std::string str(&buf[0]);
452 
453  switch(id) {
454  case 0x61: project.swap(str); break;
455  case 0x62: part.swap(str); break;
456  case 0x63: date.swap(str); break;
457  case 0x64: time.swap(str); break;
458  }
459  }
460 
461  date += " ";
462  date += time;
463 
464  this->project.swap(project);
465  this->part.swap(part);
466  this->date.swap(date);
467  return true;
468 }
469 
470 } // namespace mrf
double inc()
Definition: spi.h:88
virtual int_type underflow() OVERRIDE FINAL
Definition: flash.cpp:376
bool ok() const
Definition: spi.h:87
epicsUInt32 pageSize
PAGE PROGRAM (0x02) size in bytes. Always a power of 2.
Definition: flash.h:38
void readID(ID *id)
execute command 0x9f JEDEC-ID read
Definition: flash.cpp:33
double timeout() const
timeout in seconds for an individual cycle()
Definition: spi.cpp:41
CFIFlash(const SPIDevice &dev)
Definition: flash.cpp:25
virtual void cycles(size_t nops, const Operation *ops)
Definition: spi.cpp:25
virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) OVERRIDE FINAL
Definition: flash.cpp:398
epicsUInt32 capacity
total capacity in bytes
Definition: flash.h:38
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
epicsUInt8 * out
Definition: spi.h:35
epicsUInt8 dev_type
Definition: flash.h:33
Definition: devObj.h:97
const epicsUInt8 * in
Definition: spi.h:34
SPIInterface * interface() const
Definition: spi.h:59
epicsUInt8 vendor
Definition: flash.h:33
bool read(std::istream &strm)
Definition: flash.cpp:405
void write(epicsUInt32 start, epicsUInt32 count, const epicsUInt8 *out, bool strict=true)
Definition: flash.cpp:157
CFIStreamBuf(CFIFlash &flash)
Definition: flash.cpp:371
Definition: flash.cpp:23
void erase(epicsUInt32 start, epicsUInt32 count, bool strict=true)
Definition: flash.cpp:274
void read(epicsUInt32 start, epicsUInt32 count, epicsUInt8 *in)
Definition: flash.cpp:124
virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode mode) OVERRIDE FINAL
Definition: flash.cpp:388
epicsUInt32 pageSize()
Definition: flash.h:53