Xmega IEC60730 Class B Library  1.0
 All Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
classb_crc_hw.c
Go to the documentation of this file.
1 /**
2  * \file
3  *
4  * \brief Driver for 16- and 32-bit CRC using CRC hardware module.
5  *
6  * Copyright (C) 2012 Atmel Corporation. All rights reserved.
7  *
8  * \page License
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright notice,
14  * this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright notice,
17  * this list of conditions and the following disclaimer in the documentation
18  * and/or other materials provided with the distribution.
19  *
20  * 3. The name of Atmel may not be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * 4. This software may only be redistributed and used in connection with an
24  * Atmel AVR product.
25  *
26  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
27  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
29  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
30  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
36  * DAMAGE.
37  */
38 
39 #include "classb_crc_hw.h"
40 
41 //! \ingroup classb_crc_hw
42 //@{
43 
44 /******************* CRC DRIVER Common ******************/
45 
46 #if defined(CLASSB_CRC_USE_HW) || defined(__DOXYGEN__)
47 
48 
49 //! \internal
50 //!
51 //! \brief Initial CRC value to use on next CRC calculation.
52 uint32_t crc_initial_value = 0;
53 
54 
55 /**
56  * \internal
57  *
58  * \brief Non-Volatile Memory Execute Specific Command
59  *
60  * This function sets a command in the NVM.CMD register, then performs an
61  * execute command by writing the CMDEX bit to the NVM.CTRLA register.
62  *
63  * \note The function saves and restores the NVM.CMD register, but if this
64  * function is called from an interrupt, interrupts must be disabled
65  * before this function is called.
66  *
67  * \param nvm_command NVM Command to execute.
68  */
69 static inline void nvm_issue_command(NVM_CMD_t nvm_command)
70 {
71  uint8_t old_cmd;
72 
73  old_cmd = NVM.CMD;
74  NVM.CMD = nvm_command;
75 
76  CCP = CCP_IOREG_gc;
77  NVM.CTRLA = NVM_CMDEX_bm;
78 
79  NVM.CMD = old_cmd;
80 }
81 
82 /**
83  * \internal
84  *
85  * \brief Check if the CRC module is busy
86  *
87  * This function can be used to see if a CRC checksum generation is completed.
88  *
89  * \retval true if module is busy
90  * \retval false if module is not busy
91  *
92  */
93 static inline bool crc_is_busy(void)
94 {
95  return ((CRC_STATUS & CRC_BUSY_bm) == CRC_BUSY_bm);
96 }
97 
98 /**
99  * \internal
100  *
101  * \brief Add one byte of data to CRC performed on I/O interface
102  *
103  * This function writes a byte to the DATAIN register. Each time this function
104  * is called, the checksum is calculated for the new data appended to all
105  * previous data written since the \ref crc_io_checksum_byte_start was called.
106  * A new checksum is ready one clock cycle after the DATAIN register is
107  * written.
108  *
109  * \param data data to perform CRC on
110  *
111  */
112 void crc_io_checksum_byte_add(uint8_t data)
113 {
114  CRC_DATAIN = data;
115 }
116 
117 /**
118  * \internal
119  *
120  * \brief Reset CRC module and set CHECKSUM to initial value
121  *
122  * The CRC registers will be reset one peripheral clock cycle after the
123  * RESET[1] bit is set. The initial value is reset to 0 after loading it into
124  * the CHECKSUM registers.
125  *
126  */
127 static inline void crc_reset(void)
128 {
129  // Reset module
130  CRC_CTRL |= CRC_RESET_RESET0_gc;
131 
132  // Set initial checksum value
133  CRC.CHECKSUM0 = crc_initial_value & 0xFF;
134  CRC.CHECKSUM1 = (crc_initial_value >> 8) & 0xFF;
135  CRC.CHECKSUM2 = (crc_initial_value >> 16) & 0xFF;
136  CRC.CHECKSUM3 = (crc_initial_value >> 24) & 0xFF;
137 
138  crc_initial_value = 0;
139 }
140 
141 
142 /**
143  * \internal
144  *
145  * \brief Read CRC-16 checksum
146  *
147  * This function returns the computed CRC-16 checksum.
148  *
149  * \return checksum checksum value
150  */
151 static inline uint16_t crc16_checksum_read(void)
152 {
153  uint16_t checksum;
154  checksum = 0;
155  checksum = ((uint16_t)CRC_CHECKSUM0 & 0x00FF);
156  checksum |= (((uint16_t)CRC_CHECKSUM1 << 8) & 0xFF00);
157 
158  return checksum;
159 }
160 
161 /**
162  * \internal
163  *
164  * \brief Read CRC-32 checksum
165  *
166  * This function extracts the CRC-32 checksum from the registers.
167  *
168  * \note In IEEE 802.3 CRC-32 the checksum is bit reversed
169  * and complemented.
170  *
171  * \return checksum checksum value
172  */
173 static inline uint32_t crc32_checksum_read(void)
174 {
175  uint32_t checksum;
176 
177  checksum = ((uint32_t)CRC_CHECKSUM0 & 0x000000FF);
178  checksum |= (((uint32_t)CRC_CHECKSUM1 << 8) & 0x0000FF00);
179  checksum |= (((uint32_t)CRC_CHECKSUM2 << 16) & 0x00FF0000);
180  checksum |= (((uint32_t)CRC_CHECKSUM3 << 24) & 0xFF000000);
181 
182  return checksum;
183 }
184 
185 /**
186  * \internal
187  *
188  * \brief Read checksum
189  *
190  * This function waits until the CRC conversion is complete and returns
191  * either the 32 bit or 16 bit checksum as a 32 bit value.
192  *
193  * \note This function must not be called before a IO CRC calculation is
194  * completed or a FLASH or DMA CRC calculation is started, as this will cause
195  * it to wait for the BUSY flag to be cleared forever.
196  *
197  * \return checksum checksum value
198  */
199 static inline uint32_t crc_checksum_read(void)
200 {
201  // Wait until the CRC conversion is finished
202  while (crc_is_busy()) {
203  // Do nothing
204  }
205 
206  // Check if we have a 32 or 16 bit checksum and return the correct one
207  if ((CRC_CTRL & CRC_CRC32_bm) == CRC_CRC32_bm) {
208  return crc32_checksum_read();
209  } else {
210  return crc16_checksum_read();
211  }
212 }
213 
214 /**
215  * \internal
216  *
217  * \brief Set data source for the CRC module after reset
218  *
219  * The selected source is locked until either the CRC generation is completed
220  * or the CRC module is reset.
221  * CRC generation complete is generated and signaled from the selected source
222  * when used with the DMA controller or flash memory.
223  * To disable the CRC module, use CRC_SOURCE_DISABLE_gc as source.
224  *
225  * \param source the data source for the CRC module
226  */
227 static inline void crc_set_source(CRC_SOURCE_t source)
228 {
229  CRC_CTRL &= ~CRC_SOURCE_gm;
230  CRC_CTRL |= source;
231 }
232 
233 
234 
235 /**
236  * \internal
237  *
238  * \brief Disable the CRC module
239  *
240  */
241 static inline void crc_disable(void)
242 {
243  crc_set_source(CRC_SOURCE_DISABLE_gc);
244 }
245 
246 /**
247  * \internal
248  *
249  * \brief Complete CRC calculation by reading checksum and disabling module
250  *
251  * \return checksum checksum value
252  */
253 static inline uint32_t crc_checksum_complete(void)
254 {
255  uint32_t checksum;
256 
257  // Read checksum
258  checksum = crc_checksum_read();
259 
260  // Disable CRC module
261  crc_disable();
262 
263  // Return checksum
264  return checksum;
265 }
266 
267 
268 /**
269  * \internal
270  *
271  * \brief Set initial CRC value for next CRC calculation
272  *
273  * \note Value is reset to 0 when CRC calculation completes.
274  *
275  * \param value Initial CRC value
276  */
277 void crc_set_initial_value(uint32_t value)
278 {
279  crc_initial_value = value;
280 }
281 
282 
283 /**
284  * \internal
285  *
286  * \brief Calculate the CRC checksum for data in a buffer
287  *
288  * This function initializes the CRC module, calculates the checksum for the
289  * selected number of bytes in a data buffer, disables the module and returns
290  * the calculated checksum.
291  *
292  * \param data data buffer to perform CRC on
293  * \param len the number of bytes to perform CRC on
294  * \param crc_16_32 enum to indicate whether CRC-32 or CRC-16 shall be used
295  *
296  * \return checksum checksum
297  */
298 uint32_t crc_io_checksum(void *data, uint16_t len, enum crc_16_32_t crc_16_32)
299 {
300  // Initialize CRC calculations on I/O interface
301  crc_io_checksum_byte_start(crc_16_32);
302 
303  // Write data to DATAIN register
304  while (len--) {
305  crc_io_checksum_byte_add(*(uint8_t*)data);
306  data = (uint8_t*)data + 1;
307  }
308 
309  // Return checksum
310  return crc_io_checksum_byte_stop();
311 }
312 
313 
314 /**
315  * \internal
316  *
317  * \brief Enable CRC-32
318  *
319  * This function will set the CRC-32 polynomial to be used to generate
320  * the checksum.
321  *
322  * \note This cannot be changed while the busy flag is set.
323  *
324  */
325 static inline void crc_32_enable(void)
326 {
327  CRC_CTRL |= CRC_CRC32_bm;
328 }
329 
330 
331 /**
332  * \internal
333  *
334  * \brief Reset CRC module and set source to I/O interface
335  *
336  * This function initializes the CRC module, and \ref crc_io_checksum_byte_add
337  * can be used to add bytes to calculate checksum for. When any number of bytes
338  * have been added, the \ref crc_io_checksum_byte_stop can be called to disable
339  * the module and get the calculated checksum.
340  *
341  * \param crc_16_32 enum to indicate whether CRC-32 or CRC-16 shall be used
342  */
343 void crc_io_checksum_byte_start(enum crc_16_32_t crc_16_32)
344 {
345  // Initialize CRC calculations on I/O interface
346  crc_reset();
347  // Enable CRC-32 if chosen
348  if (crc_16_32 == CRC_32BIT) {
349  crc_32_enable();
350  }
351  // Enable CRC module using the I/O interface
352  crc_set_source(CRC_SOURCE_IO_gc);
353 }
354 
355 /**
356  * \internal
357  *
358  * \brief Disable CRC module and return checksum
359  *
360  * This function stops the CRC calculation for bytes added with the \ref
361  * crc_io_checksum_byte_add and returns the calculated checksum.
362  *
363  * \retval checksum checksum
364  *
365  */
366 uint32_t crc_io_checksum_byte_stop(void)
367 {
368  // Signal CRC complete
369  CRC_STATUS |= CRC_BUSY_bm;
370 
371  // Stop CRC and return checksum
372  return crc_checksum_complete();
373 }
374 
375 
376 #endif // defined(CLASSB_CRC_USE_HW)
377 
378 
379 /******************* CRC16 Functions ******************/
380 
381 #if (defined(CLASSB_CRC_USE_HW) && defined(CLASSB_CRC_16_BIT)) || defined(__DOXYGEN__)
382 
383 
384 /*! \brief Compute 16-bit CRC for EEPROM address range using hardware CRC module.
385  *
386  * This function returns the 16-bit CRC of the specified EEPROM address range.
387  *
388  * \param origDataptr Address of EEPROM location to start CRC computation at.
389  * \param numBytes Number of bytes of the data.
390  * \param pchecksum Pointer to the checksum stored in EEPROM.
391  *
392  * \note No sanity checking of addresses is done.
393  */
394 uint16_t CLASSB_CRC16_EEPROM_HW (eepromptr_t origDataptr, crcbytenum_t numBytes, eeprom_uint16ptr_t pchecksum)
395 {
396  eeprom_uint8ptr_t dataptr = origDataptr;
397 
398  crc_set_initial_value(CRC16_INITIAL_REMAINDER);
399 
400  // Ensure that EEPROM is memory mapped.
402  dataptr += MAPPED_EEPROM_START;
403 
404  uint32_t checksum = crc_io_checksum((void*)dataptr, numBytes, CRC_16BIT);
405 
406 
407  #if defined(__ICCAVR__)
408  // Disable memory mapping of EEPROM, if necessary.
410  // Compare checksums and handle error if necessary.
411  if ( checksum != *pchecksum)
413  #elif defined(__GNUC__)
414  // Compare checksums and handle error if necessary.
415  if ( checksum != *(eeprom_uint16ptr_t)(MAPPED_EEPROM_START + (uintptr_t) pchecksum))
417  // Disable memory mapping of EEPROM, if necessary.
418  CLASSB_EEMAP_END();
419  #endif
420 
421 
422 
423  // Return 16 bits
424  return (checksum & 0x0000FFFF);
425 }
426 
427 
428 /*! \brief Compute 16-bit CRC for Flash address range using hardware CRC module.
429  *
430  * This function returns the 16-bit CRC of the specified Flash address range.
431  *
432  * \param origDataptr Address of Flash location to start CRC computation at.
433  * \param numBytes Number of bytes of the data.
434  * \param pchecksum Pointer to the checksum stored in EEPROM.
435  *
436  * \note 16-bit flash CRC is much slower than 32-bit for flash checking.
437  * \note No sanity checking of addresses is done.
438  */
439 uint16_t CLASSB_CRC16_Flash_HW (flashptr_t origDataptr, crcbytenum_t numBytes, eeprom_uint16ptr_t pchecksum )
440 {
441  flash_uint8ptr_t dataptr = origDataptr;
442  uint8_t dataTemp;
443 
444  crc_set_initial_value(CRC16_INITIAL_REMAINDER);
445 
446  crc_io_checksum_byte_start(CRC_16BIT);
447 
448  // Compute CRC for the specified data.
449  for (; numBytes != 0; numBytes--) {
450  #if (PROGMEM_SIZE >= 0x10000UL)
451  dataTemp = PROGMEM_READ_BYTE_FAR(dataptr++);
452  #else
453  dataTemp = PROGMEM_READ_BYTE(dataptr++);
454  #endif
455 
456  crc_io_checksum_byte_add(dataTemp);
457  }
458 
459  uint32_t checksum = crc_io_checksum_byte_stop();
460 
461  #if defined(__ICCAVR__)
462  // Compare checksums and handle error if necessary.
463  if ( checksum != *pchecksum)
465  #elif defined(__GCC__)
466  // Ensure that EEPROM is memory mapped.
468  // Compare checksums and handle error if necessary.
469  if ( checksum != *(eeprom_uint16ptr_t)(MAPPED_EEPROM_START + (uintptr_t) pchecksum))
471  // Disable memory mapping of EEPROM, if necessary.
473  #endif
474 
475  return (checksum & 0x0000FFFF);
476 }
477 
478 
479 
480 
481 /**
482  * \internal
483  *
484  * \brief Appends a CRC value to the given pointer
485  *
486  * Taking the checksum of the value and the appended data will result in zero.
487  *
488  * \note At least two bytes must be reserved at the given pointer location.
489  *
490  * \param value CRC value to append
491  * \param ptr pointer to 2 byte memory location to store value.
492  *
493  */
494 void crc16_append_value(uint16_t value, void *ptr)
495 {
496  *(uint8_t*)ptr = (value >> 8);
497  *((uint8_t*)ptr+1) = value;
498 }
499 
500 #endif // defined(CLASSB_CRC_USE_HW) && defined(CLASSB_CRC_16_BIT)
501 
502 
503 /******************* CRC32 Functions ******************/
504 
505 #if (defined(CLASSB_CRC_USE_HW) && defined(CLASSB_CRC_32_BIT)) || defined(__DOXYGEN__)
506 
507 /**
508  * \brief Compute 32-bit CRC for EEPROM address range using hardware CRC module.
509  *
510  * This function returns the 32-bit CRC of the specified EEPROM address range.
511  *
512  * \param origDataptr Address of EEPROM location to start CRC computation at.
513  * \param numBytes Number of bytes of the data.
514  * \param pchecksum Pointer to the checksum stored in EEPROM.
515  *
516  * \note No sanity checking of addresses is done.
517  */
518 uint32_t CLASSB_CRC32_EEPROM_HW (eepromptr_t origDataptr, crcbytenum_t numBytes, eeprom_uint32ptr_t pchecksum)
519 {
520  eeprom_uint8ptr_t dataptr = origDataptr;
521  crc_set_initial_value(CRC32_INITIAL_REMAINDER);
522 
523  // Ensure that EEPROM is memory mapped.
525  dataptr += MAPPED_EEPROM_START;
526 
527  uint32_t checksum = crc_io_checksum((void*)dataptr, numBytes, CRC_32BIT);
528 
529  #if defined(__ICCAVR__)
530  // Disable memory mapping of EEPROM, if necessary.
532  // Compare checksums and handle error if necessary.
533  if ( checksum != *pchecksum)
535  #elif defined(__GCC__)
536  // Compare checksums and handle error if necessary.
537  if ( checksum != *(eeprom_uint32ptr_t)(MAPPED_EEPROM_START + (uintptr_t) pchecksum))
539  // Disable memory mapping of EEPROM, if necessary.
541  #endif
542 
543 
544 
545  return (checksum);
546 }
547 
548 
549 /** \brief Compute 32-bit CRC for Flash address range using hardware CRC module.
550  *
551  * This function returns the 32-bit CRC of the specified Flash address range.
552  *
553  * \param crc_type Type of CRC computation: \ref CRC_FLASH_RANGE, \ref CRC_BOOT or \ref CRC_APP.
554  * \param origDataptr Address of Flash location to start CRC computation at.
555  * \param numBytes Number of bytes of the data.
556  * \param pchecksum Pointer to the checksum stored in EEPROM.
557  *
558  * \note No sanity checking of addresses is done.
559  */
560 uint32_t CLASSB_CRC32_Flash_HW (NVM_CMD_t crc_type, flashptr_t origDataptr, crcbytenum_t numBytes, eeprom_uint32ptr_t pchecksum)
561 {
562  flash_uint8ptr_t dataptr = origDataptr;
563  crc_set_initial_value(CRC32_INITIAL_REMAINDER);
564 
565  // Compute CRC for the specified data.
566  uint32_t checksum = crc_flash_checksum(crc_type, (flash_addr_t)dataptr, numBytes);
567 
568  #if defined(__ICCAVR__)
569  // Compare checksums and handle error if necessary.
570  if ( checksum != *pchecksum)
572  #elif defined(__GNUC__)
573  // Ensure that EEPROM is memory mapped.
575  // Compare checksums and handle error if necessary.
576  if ( checksum != *(eeprom_uint32ptr_t)(MAPPED_EEPROM_START + (uintptr_t) pchecksum))
578  // Disable memory mapping of EEPROM, if necessary.
580  #endif
581 
582  return(checksum);
583 }
584 
585 
586 /**
587  * \internal
588  *
589  * \brief Get and append data to generate checksum zero with CRC32
590  *
591  * This function calculates checksum value to append to the data after
592  * generating its checksum.
593  * The value is written to given pointer as little endian.
594  * Taking the checksum of the data appended will result in checksum zero.
595  *
596  * \note At least four bytes must be reserved at the given pointer location.
597  *
598  * \param value the value to convert to append value
599  * \param ptr pointer to 4 byte memory location to store the value.
600  *
601  */
602 void crc32_append_value(uint32_t value, void *ptr)
603 {
604  *(uint8_t*)ptr = value & 0xFF;
605  *((uint8_t*)ptr+1) = (value >> 8) & 0xFF;
606  *((uint8_t*)ptr+2) = (value >> 16) & 0xFF;
607  *((uint8_t*)ptr+3) = (value >> 24) & 0xFF;
608 }
609 
610 
611 /**
612  * \internal
613  *
614  * \brief Perform a CRC-32 calculation on the entire flash memory, on only
615  * the application section, on only the boot section, or on a selectable range
616  * of the flash memory.
617  *
618  * This function setups the type of CRC to perform and configures the memory
619  * range to perform the CRC on, and starts the CRC via the NVM module.
620  * When the calculation is done, the function disables the CRC module and
621  * returns the checksum.
622  * For CRC on the boot or application section, start address and length are not
623  * needed.
624  *
625  * \note In the flash range mode, an even number of bytes is read. If the user
626  * selects a range with an odd number of bytes, an extra byte will be read, and
627  * the checksum will not correspond to the selected range.
628  *
629  * \param crc_type what kind of CRC to do perform on the nvm module: \ref
630  * CRC_FLASH_RANGE, \ref CRC_BOOT or \ref CRC_APP
631  * \param flash_addr address of first byte in flash to perform CRC on
632  * \param length number of bytes to perform CRC on
633  *
634  * \return checksum CRC-32 checksum
635  */
636 uint32_t crc_flash_checksum(NVM_CMD_t crc_type, flash_addr_t flash_addr, uint32_t length)
637 {
638  if ((length == 0) && (crc_type == CRC_FLASH_RANGE))
639  return 0;
640 
641  if (! ((crc_type != CRC_FLASH_RANGE) || (crc_type != CRC_BOOT) ||
642  (crc_type != CRC_APP)) )
643  return 0;
644 
645  // Initialize CRC for flash
646  crc_reset();
647  // Set CRC32 enable bit to ensure correct reading of the resulting checksum
648  crc_32_enable();
649  crc_set_source(CRC_SOURCE_FLASH_gc);
650 
651  if (crc_type == CRC_FLASH_RANGE) {
652  nvm_issue_flash_range_crc(flash_addr, flash_addr + length - 1);
653  } else {
654  nvm_issue_command(crc_type);
655  }
656 
657  // Disable CRC and return checksum
658  return crc_checksum_complete();
659 }
660 
661 
662 /**
663  * \internal
664  *
665  * \brief Issue flash range CRC command
666  *
667  * This function sets the FLASH range CRC command in the NVM.CMD register.
668  * It then loads the start and end byte address of the part of FLASH to
669  * generate a CRC-32 for into the ADDR and DATA registers and finally performs
670  * the execute command.
671  *
672  * \note Should only be called from the CRC module. The function saves and
673  * restores the NVM.CMD register, but if this
674  * function is called from an interrupt, interrupts must be disabled
675  * before this function is called.
676  *
677  * \param start_addr end byte address
678  * \param end_addr start byte address
679  */
680 void nvm_issue_flash_range_crc(flash_addr_t start_addr, flash_addr_t end_addr)
681 {
682  uint8_t old_cmd;
683  // Save current nvm command
684  old_cmd = NVM.CMD;
685 
686  // Load the NVM CMD register with the Flash Range CRC command
687  NVM.CMD = NVM_CMD_FLASH_RANGE_CRC_gc;
688 
689  // Load the start byte address in the NVM Address Register
690  NVM.ADDR0 = start_addr & 0xFF;
691  NVM.ADDR1 = (start_addr >> 8) & 0xFF;
692 #if (PROGMEM_SIZE >= 0x10000UL)
693  NVM.ADDR2 = (start_addr >> 16) & 0xFF;
694 #endif
695 
696  // Load the end byte address in NVM Data Register
697  NVM.DATA0 = end_addr & 0xFF;
698  NVM.DATA1 = (end_addr >> 8) & 0xFF;
699 #if (PROGMEM_SIZE >= 0x10000UL)
700  NVM.DATA2 = (end_addr >> 16) & 0xFF;
701 #endif
702 
703  // Execute command
704  CCP = CCP_IOREG_gc;
705  NVM.CTRLA = NVM_CMDEX_bm;
706 
707  // Restore command register
708  NVM.CMD = old_cmd;
709 }
710 
711 #endif //defined(CLASSB_CRC_USE_HW) && defined(CLASSB_CRC_32_BIT)
712 
713 //@}
714