wg_eeprom.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2008, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
36 #include "ros/ros.h"
37 
38 #include <boost/static_assert.hpp>
39 #include <boost/thread/mutex.hpp>
40 #include <boost/thread.hpp>
41 
42 namespace ethercat_hardware
43 {
44 
45 
47 {
48  uint16_t page_;
49  union
50  {
51  uint8_t command_;
52  struct
53  {
54  uint8_t operation_ :4;
55  uint8_t start_ :1;
56  uint8_t busy_ :1;
57  uint8_t unused2_ :2;
58  }__attribute__ ((__packed__));
59  };
60 
61  void build_read(unsigned page)
62  {
63  this->page_ = page & 0xffff;
64  this->operation_ = SPI_READ_OP;
65  this->start_ = 1;
66  }
67  void build_write(unsigned page)
68  {
69  this->page_ = page & 0xffff;
70  this->operation_ = SPI_WRITE_OP;
71  this->start_ = 1;
72  }
73  void build_arbitrary(unsigned length)
74  {
75  this->page_ = (length-1) & 0xffff;
77  this->start_ = 1;
78  }
79 
80  static const unsigned SPI_READ_OP = 0;
81  static const unsigned SPI_WRITE_OP = 1;
82  static const unsigned SPI_ARBITRARY_OP = 3;
83 
84  static const unsigned SPI_COMMAND_ADDR = 0x0230;
85  static const unsigned SPI_BUFFER_ADDR = 0xF400;
86 }__attribute__ ((__packed__));
87 
88 
89 
91 {
92  union {
93  uint8_t raw_;
94  struct {
95  uint8_t page_size_ : 1;
96  uint8_t write_protect_ : 1;
97  uint8_t eeprom_size_ : 4;
98  uint8_t compare_ : 1;
99  uint8_t ready_ : 1;
100  } __attribute__ ((__packed__));
101  } __attribute__ ((__packed__));
102 } __attribute__ ((__packed__));
103 
104 
105 
106 
108 {
109 
110 }
111 
112 
122 {
124  // TODO : poll for a given number of millseconds instead of a given number of cycles
125  //start_time = 0;
126  unsigned tries = 0;
127  do {
128  //read_time = time;
129  ++tries;
130  if (!readSpiEepromCmd(com, mbx, cmd))
131  {
132  ROS_ERROR("Error reading SPI Eeprom Cmd busy bit");
133  return false;
134  }
135 
136  if (!cmd.busy_)
137  {
138  return true;
139  }
140 
141  usleep(100);
142  } while (tries <= 10);
143 
144  ROS_ERROR("Timed out waiting for SPI state machine to be idle (%d)", tries);
145  return false;
146 }
147 
148 
159 {
160  if (!waitForSpiEepromReady(com, mbx))
161  {
162  return false;
163  }
164 
165  // Send command
166  if (mbx->writeMailbox(com, WG0XSpiEepromCmd::SPI_COMMAND_ADDR, &cmd, sizeof(cmd)))
167  {
168  ROS_ERROR("Error writing SPI EEPROM command");
169  return false;
170  }
171 
172  // Now read back SPI EEPROM state machine register, and check :
173  // 1. for state machine to become ready
174  // 2. that command data was properly write and not corrupted
175  WG0XSpiEepromCmd stat;
176  unsigned tries = 0;
177  do
178  {
179  if (!readSpiEepromCmd(com, mbx, stat))
180  {
181  return false;
182  }
183 
184  if (stat.operation_ != cmd.operation_)
185  {
186  ROS_ERROR("Invalid readback of SPI EEPROM operation : got 0x%X, expected 0x%X\n", stat.operation_, cmd.operation_);
187  return false;
188  }
189 
190  // return true if command has completed
191  if (!stat.busy_)
192  {
193  if (tries > 0)
194  {
195  ROS_WARN("Eeprom state machine took %d cycles", tries);
196  }
197  return true;;
198  }
199 
200  fprintf(stderr, "eeprom busy reading again, waiting...\n");
201  usleep(100);
202  } while (++tries < 10);
203 
204  ROS_ERROR("Eeprom SPI state machine busy after %d cycles", tries);
205  return false;
206 }
207 
208 
223 bool WGEeprom::readEepromPage(EthercatCom *com, WGMailbox *mbx, unsigned page, void* data, unsigned length)
224 {
225  boost::lock_guard<boost::mutex> lock(mutex_);
226 
227  if (length > MAX_EEPROM_PAGE_SIZE)
228  {
229  ROS_ERROR("Eeprom read length %d > %d", length, MAX_EEPROM_PAGE_SIZE);
230  return false;
231  }
232 
233  if (page >= NUM_EEPROM_PAGES)
234  {
235  ROS_ERROR("Eeprom read page %d > %d", page, NUM_EEPROM_PAGES-1);
236  return false;
237  }
238 
239  // Since we don't know the size of the eeprom there is not always 264 bytes available.
240  // This may try to read 264 bytes, but only the first 256 bytes may be valid.
241  // To avoid any odd issue, zero out FPGA buffer before asking for eeprom data.
242  memset(data,0,length);
243  if (mbx->writeMailbox(com, WG0XSpiEepromCmd::SPI_BUFFER_ADDR, data, length))
244  {
245  ROS_ERROR("Error zeroing eeprom data buffer");
246  return false;
247  }
248 
249  // Send command to SPI state machine to perform read of eeprom,
250  // sendSpiEepromCmd will automatically wait for SPI state machine
251  // to be idle before a new command is sent
253  memset(&cmd,0,sizeof(cmd));
254  cmd.build_read(page);
255  if (!sendSpiEepromCmd(com, mbx, cmd))
256  {
257  ROS_ERROR("Error sending SPI read command");
258  return false;
259  }
260 
261  // Wait for SPI Eeprom Read to complete
262  // sendSPICommand will wait for Command to finish before returning
263 
264  // Read eeprom page data from FPGA buffer
265  if (mbx->readMailbox(com, WG0XSpiEepromCmd::SPI_BUFFER_ADDR, data, length))
266  {
267  ROS_ERROR("Error reading eeprom data from buffer");
268  return false;
269  }
270 
271  return true;
272 }
273 
274 
289 bool WGEeprom::writeEepromPage(EthercatCom *com, WGMailbox *mbx, unsigned page, const void* data, unsigned length)
290 {
291  boost::lock_guard<boost::mutex> lock(mutex_);
292 
293  if (length > 264)
294  {
295  ROS_ERROR("Eeprom write length %d > %d", length, MAX_EEPROM_PAGE_SIZE);
296  return false;
297  }
298 
299  if (page >= NUM_EEPROM_PAGES)
300  {
301  ROS_ERROR("Eeprom write page %d > %d", page, NUM_EEPROM_PAGES-1);
302  return false;
303  }
304 
305  // wait for eeprom to be ready before write data into FPGA buffer
306  if (!waitForSpiEepromReady(com, mbx))
307  {
308  return false;
309  }
310 
311  const void *write_buf = data;
312 
313  // if needed, pad data to 264 byte in buf
314  uint8_t buf[MAX_EEPROM_PAGE_SIZE];
315  if (length < MAX_EEPROM_PAGE_SIZE)
316  {
317  memcpy(buf, data, length);
318  memset(buf+length, 0xFF, MAX_EEPROM_PAGE_SIZE-length);
319  write_buf = buf;
320  }
321 
322  // Write data to FPGA buffer
323  if (mbx->writeMailbox(com, WG0XSpiEepromCmd::SPI_BUFFER_ADDR, write_buf, MAX_EEPROM_PAGE_SIZE))
324  {
325  ROS_ERROR("Write of SPI EEPROM buffer failed");
326  return false;
327  }
328 
329  // Have SPI EEPROM state machine start SPI data transfer
331  cmd.build_write(page);
332  if (!sendSpiEepromCmd(com, mbx, cmd))
333  {
334  ROS_ERROR("Error giving SPI EEPROM write command");
335  return false;
336  }
337 
338  // Wait for EEPROM write to complete
339  if (!waitForEepromReady(com, mbx))
340  {
341  return false;
342  }
343 
344  return true;
345 }
346 
347 
360 {
361  // Wait for eeprom write to complete
362  unsigned tries = 0;
363  EepromStatusReg status_reg;
364  do {
365  if (!readEepromStatusReg(com, mbx, status_reg))
366  {
367  return false;
368  }
369  if (status_reg.ready_)
370  {
371  break;
372  }
373  usleep(100);
374  } while (++tries < 20);
375 
376  if (!status_reg.ready_)
377  {
378  ROS_ERROR("Eeprom still busy after %d cycles", tries);
379  return false;
380  }
381 
382  if (tries > 10)
383  {
384  ROS_WARN("EEPROM took %d cycles to be ready", tries);
385  }
386  return true;
387 }
388 
389 
390 
402 {
403  // Status is read from EEPROM by having SPI state machine perform an "abitrary" operation.
404  // With an arbitrary operation, the SPI state machine shifts out byte from buffer, while
405  // storing byte shifted in from device into same location in buffer.
406  // SPI state machine has no idea what command it is sending device or how to intpret its result.
407 
408  // To read eeprom status register, we transfer 2 bytes. The first byte is the read status register
409  // command value (0xD7). When transfering the second byte, the EEPROM should send its status.
410  unsigned char data[2] = {0xD7, 0x00};
411  BOOST_STATIC_ASSERT(sizeof(data) == 2);
412  if (mbx->writeMailbox(com, WG0XSpiEepromCmd::SPI_BUFFER_ADDR, data, sizeof(data)))
413  {
414  ROS_ERROR("Writing SPI buffer");
415  return false;
416  }
417 
418  { // Have SPI state machine trasfer 2 bytes
420  cmd.build_arbitrary(sizeof(data));
421  if (!sendSpiEepromCmd(com, mbx, cmd))
422  {
423  ROS_ERROR("Sending SPI abitrary command");
424  return false;
425  }
426  }
427 
428  // Data read from device should now be stored in FPGA buffer
429  if (mbx->readMailbox(com, WG0XSpiEepromCmd::SPI_BUFFER_ADDR, data, sizeof(data)))
430  {
431  ROS_ERROR("Reading status register data from SPI buffer");
432  return false;
433  }
434 
435  // Status register would be second byte of buffer
436  reg.raw_ = data[1];
437  return true;
438 }
439 
440 
457 {
458  BOOST_STATIC_ASSERT(sizeof(WG0XSpiEepromCmd) == 3);
459  if (mbx->readMailbox(com, WG0XSpiEepromCmd::SPI_COMMAND_ADDR, &cmd, sizeof(cmd)))
460  {
461  ROS_ERROR("Reading SPI command register with mailbox");
462  return false;
463  }
464 
465  return true;
466 }
467 
468 
469 }; //end namespace ethercat_hardware
bool waitForEepromReady(EthercatCom *com, WGMailbox *mbx)
Waits for EEPROM to become ready.
Definition: wg_eeprom.cpp:359
uint8_t ready_
Definition: wg_eeprom.cpp:98
uint8_t eeprom_size_
Definition: wg_eeprom.cpp:96
uint16_t length
Definition: wg_util.h:119
bool sendSpiEepromCmd(EthercatCom *com, WGMailbox *mbx, const WG0XSpiEepromCmd &cmd)
Sends command to SPI EEPROM state machine.
Definition: wg_eeprom.cpp:158
string cmd
static const unsigned SPI_WRITE_OP
Definition: wg_eeprom.cpp:81
bool readSpiEepromCmd(EthercatCom *com, WGMailbox *mbx, WG0XSpiEepromCmd &cmd)
Reads SPI state machine command register.
Definition: wg_eeprom.cpp:456
data
uint8_t compare_
Definition: wg_eeprom.cpp:97
#define ROS_WARN(...)
bool writeEepromPage(EthercatCom *com, WGMailbox *mbx, unsigned page, const void *data, unsigned length)
Write data to single eeprom page.
Definition: wg_eeprom.cpp:289
void build_arbitrary(unsigned length)
Definition: wg_eeprom.cpp:73
int writeMailbox(EthercatCom *com, unsigned address, void const *data, unsigned length)
Write data to WG0X local bus using mailbox communication.
Definition: wg_mailbox.cpp:939
bool readEepromPage(EthercatCom *com, WGMailbox *mbx, unsigned page, void *data, unsigned length)
Read data from single eeprom page.
Definition: wg_eeprom.cpp:223
void build_write(unsigned page)
Definition: wg_eeprom.cpp:67
uint8_t write_protect_
Definition: wg_eeprom.cpp:95
void build_read(unsigned page)
Definition: wg_eeprom.cpp:61
static const unsigned SPI_ARBITRARY_OP
Definition: wg_eeprom.cpp:82
bool readEepromStatusReg(EthercatCom *com, WGMailbox *mbx, EepromStatusReg &reg)
Reads EEPROM status register.
Definition: wg_eeprom.cpp:401
int readMailbox(EthercatCom *com, unsigned address, void *data, unsigned length)
Read data from WG0X local bus using mailbox communication.
Definition: wg_mailbox.cpp:812
union ethercat_hardware::WG0XSpiEepromCmd::@53 __attribute__
uint8_t page_size_
Definition: wg_eeprom.cpp:94
static const unsigned SPI_COMMAND_ADDR
Definition: wg_eeprom.cpp:84
#define ROS_ERROR(...)
bool waitForSpiEepromReady(EthercatCom *com, WGMailbox *mbx)
Waits for SPI eeprom state machine to be idle.
Definition: wg_eeprom.cpp:121
static const unsigned SPI_BUFFER_ADDR
Definition: wg_eeprom.cpp:85
static const unsigned SPI_READ_OP
Definition: wg_eeprom.cpp:80


ethercat_hardware
Author(s): Rob Wheeler , Derek King
autogenerated on Thu Mar 4 2021 03:10:21