LCOV - code coverage report
Current view: top level - AP_RAMTRON - AP_RAMTRON.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 72 99 72.7 %
Date: 2026-09-09 11:02:08 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :   driver for RAMTRON FRAM persistent memory devices. These are used
       3             :   for parameter and waypoint storage on most FMUv1, FMUv2, FMUv3 and FMUv4
       4             :   boards
       5             :  */
       6             : 
       7             : #include "AP_RAMTRON.h"
       8             : #include <AP_Math/crc.h>
       9             : #include <AP_Math/AP_Math.h>
      10             : 
      11             : extern const AP_HAL::HAL &hal;
      12             : 
      13             : // register numbers
      14             : static const uint8_t RAMTRON_RDID  = 0x9f;
      15             : static const uint8_t RAMTRON_READ  = 0x03;
      16             : static const uint8_t RAMTRON_WREN  = 0x06;
      17             : static const uint8_t RAMTRON_WRITE = 0x02;
      18             : 
      19             : #define RAMTRON_RETRIES 10
      20             : #define RAMTRON_DELAY_MS 10
      21             : 
      22             : /*
      23             :   list of supported devices. Thanks to NuttX ramtron driver
      24             :  */
      25             : const AP_RAMTRON::ramtron_id AP_RAMTRON::ramtron_ids[] = {
      26             :     { 0x21, 0x00,  16, 2, RDID_type::Cypress }, // FM25V01
      27             :     { 0x21, 0x08,  16, 2, RDID_type::Cypress }, // FM25V01A
      28             :     { 0x22, 0x00,  32, 2, RDID_type::Cypress }, // FM25V02
      29             :     { 0x22, 0x08,  32, 2, RDID_type::Cypress }, // FM25V02A
      30             :     { 0x22, 0x48,  32, 2, RDID_type::Cypress }, // FM25V02A - Extended Temperature Version
      31             :     { 0x22, 0x01,  32, 2, RDID_type::Cypress }, // FM25VN02
      32             :     { 0x23, 0x00,  64, 2, RDID_type::Cypress }, // FM25V05
      33             :     { 0x23, 0x01,  64, 2, RDID_type::Cypress }, // FM25VN05
      34             :     { 0x24, 0x00, 128, 3, RDID_type::Cypress }, // FM25V10
      35             :     { 0x24, 0x01, 128, 3, RDID_type::Cypress }, // FM25VN10
      36             :     { 0x25, 0x08, 256, 3, RDID_type::Cypress }, // FM25V20A
      37             :     { 0x26, 0x08, 512, 3, RDID_type::Cypress }, // CY15B104Q
      38             : 
      39             :     { 0x27, 0x03, 128, 3, RDID_type::Fujitsu }, // MB85RS1MT
      40             :     { 0x05, 0x09,  32, 2, RDID_type::Fujitsu }, // MB85RS256B
      41             :     { 0x24, 0x03,  16, 2, RDID_type::Fujitsu }, // MB85RS128TY
      42             :     { 0x25, 0x03,  32, 2, RDID_type::Fujitsu }, // MB85RS256TY
      43             : 
      44             :     { 0x22, 0x00,  16, 2, RDID_type::Petabytes }, // PB85RS128C
      45             :     { 0x24, 0x00, 256, 3, RDID_type::Petabytes }, // PB85RS2MC
      46             : };
      47             : 
      48             : // initialise the driver
      49           3 : bool AP_RAMTRON::init(void)
      50             : {
      51           3 :     dev = hal.spi->get_device_ptr("ramtron");
      52           3 :     if (!dev) {
      53           0 :         DEV_PRINTF("No RAMTRON device\n");
      54           0 :         return false;
      55             :     }
      56             : 
      57             :     struct cypress_rdid {
      58             :         uint8_t manufacturer[6];
      59             :         uint8_t memory;
      60             :         uint8_t id1;
      61             :         uint8_t id2;
      62             :     };
      63             :     struct fujitsu_rdid {
      64             :         uint8_t manufacturer[2];
      65             :         uint8_t id1;
      66             :         uint8_t id2;
      67             :     };
      68             :     struct petabytes_rdid {
      69             :         uint8_t manufacturer[2];
      70             :         uint8_t id1;
      71             :         uint8_t id2;
      72             :     };
      73             : 
      74             :     uint8_t rdid[sizeof(cypress_rdid)];
      75             : 
      76             :     {
      77           3 :         WITH_SEMAPHORE(dev->get_semaphore());
      78           3 :         if (!dev->read_registers(RAMTRON_RDID, rdid, sizeof(rdid))) {
      79           0 :             return false;
      80             :         }
      81           3 :     }
      82             : 
      83           9 :     for (uint8_t i = 0; i < ARRAY_SIZE(ramtron_ids); i++) {
      84           9 :         if (ramtron_ids[i].rdid_type == RDID_type::Cypress) {
      85           9 :             const cypress_rdid *cypress = (const cypress_rdid *)rdid;
      86           9 :             if (ramtron_ids[i].id1 == cypress->id1 &&
      87           3 :                 ramtron_ids[i].id2 == cypress->id2) {
      88           3 :                 id = i;
      89           3 :                 break;
      90             :             }
      91           0 :         } else if (ramtron_ids[i].rdid_type == RDID_type::Fujitsu) {
      92           0 :             const fujitsu_rdid *fujitsu = (const fujitsu_rdid *)rdid;
      93           0 :             if (ramtron_ids[i].id1 == fujitsu->id1 &&
      94           0 :                 ramtron_ids[i].id2 == fujitsu->id2) {
      95           0 :                 id = i;
      96           0 :                 break;
      97             :              }
      98           0 :         } else if (ramtron_ids[i].rdid_type == RDID_type::Petabytes) {
      99           0 :             const petabytes_rdid *petabytes = (const petabytes_rdid *)rdid;
     100           0 :             if (petabytes->manufacturer[0] == 0x62 && 
     101           0 :                 petabytes->manufacturer[1] == 0x8C &&
     102           0 :                 ramtron_ids[i].id1 == petabytes->id1 &&
     103           0 :                 ramtron_ids[i].id2 == petabytes->id2) {
     104           0 :                 id = i;
     105           0 :                 break;
     106             :             }
     107             :         }
     108             :     }
     109             : 
     110           3 :     if (id == UINT8_MAX) {
     111           0 :         DEV_PRINTF("Unknown RAMTRON device\n");
     112           0 :         return false;
     113             :     }
     114             : 
     115           3 :     return true;
     116             : }
     117             : 
     118             : /*
     119             :   send a command and offset
     120             :  */
     121         782 : void AP_RAMTRON::send_offset(uint8_t cmd, uint32_t offset) const
     122             : {
     123         782 :     if (ramtron_ids[id].addrlen == 3) {
     124           0 :         uint8_t b[4] = { cmd, uint8_t((offset>>16)&0xFF), uint8_t((offset>>8)&0xFF), uint8_t(offset&0xFF) };
     125           0 :         dev->transfer(b, sizeof(b), nullptr, 0);
     126             :     } else /* len 2 */ {
     127         782 :         uint8_t b[3] = { cmd, uint8_t((offset>>8)&0xFF), uint8_t(offset&0xFF) };
     128         782 :         dev->transfer(b, sizeof(b), nullptr, 0);
     129             :     }
     130         782 : }
     131             : 
     132             : // read from device
     133         384 : bool AP_RAMTRON::read(uint32_t offset, uint8_t *buf, uint32_t size)
     134             : {
     135             :     // Don't allow reads outside of the FRAM memory.
     136             :     // NOTE: The FRAM devices will wrap back to address 0x0000 if they read past
     137             :     // the end of their internal memory, so while we'll get data back, it won't
     138             :     // be what we expect.
     139         768 :     if ((size > get_size()) ||
     140         384 :         (offset > (get_size() - size))) {
     141           0 :         return false;
     142             :     }
     143         384 :     const uint8_t maxread = 128;
     144         765 :     while (size > maxread) {
     145         381 :         if (!read(offset, buf, maxread)) {
     146           0 :             return false;
     147             :         }
     148         381 :         offset += maxread;
     149         381 :         buf += maxread;
     150         381 :         size -= maxread;
     151             :     }
     152             : 
     153         384 :     for (uint8_t r=0; r<RAMTRON_RETRIES; r++) {
     154         384 :         if (r != 0) {
     155           0 :             hal.scheduler->delay(RAMTRON_DELAY_MS);
     156             :         }
     157             :         /*
     158             :           transfer each block twice and compare with a crc. This is to
     159             :           prevent transient errors from causing parameter corruption
     160             :          */
     161             :         {
     162         384 :             WITH_SEMAPHORE(dev->get_semaphore());
     163         384 :             dev->set_chip_select(true);
     164         384 :             send_offset(RAMTRON_READ, offset);
     165         384 :             dev->transfer(nullptr, 0, buf, size);
     166         384 :             dev->set_chip_select(false);
     167         384 :         }
     168             : 
     169         384 :         uint32_t crc1 = crc_crc32(0, buf, size);
     170             : 
     171             :         {
     172         384 :             WITH_SEMAPHORE(dev->get_semaphore());
     173         384 :             dev->set_chip_select(true);
     174         384 :             send_offset(RAMTRON_READ, offset);
     175         384 :             dev->transfer(nullptr, 0, buf, size);
     176         384 :             dev->set_chip_select(false);
     177         384 :         }
     178         384 :         uint32_t crc2 = crc_crc32(0, buf, size);
     179             : 
     180         384 :         if (crc1 == crc2) {
     181             :             // all good
     182         384 :             return true;
     183             :         }
     184             :     }
     185             : 
     186           0 :     return false;
     187             : }
     188             : 
     189             : // write to device
     190       95757 : bool AP_RAMTRON::write(uint32_t offset, const uint8_t *buf, uint32_t size)
     191             : {
     192             :     // Don't allow writes outside of the FRAM memory.
     193             :     // NOTE: The FRAM devices will wrap back to address 0x0000 if they write past
     194             :     // the end of their internal memory, so we could accidentally overwrite the
     195             :     // wrong memory location.
     196       95764 :     if ((size > get_size()) ||
     197           7 :         (offset > (get_size() - size))) {
     198       95750 :         return false;
     199             :     }
     200             : 
     201           7 :     WITH_SEMAPHORE(dev->get_semaphore());
     202             : 
     203           7 :     for (uint8_t r=0; r<RAMTRON_RETRIES; r++) {
     204           7 :         if (r != 0) {
     205           0 :             hal.scheduler->delay(RAMTRON_DELAY_MS);
     206             :         }
     207             : 
     208             :         // we need to enable writes every time. The WREN bit is
     209             :         // automatically reset on completion of the write call
     210           7 :         dev->set_chip_select(true);
     211           7 :         dev->transfer(&RAMTRON_WREN, 1, nullptr, 0);
     212           7 :         dev->set_chip_select(false);
     213             : 
     214           7 :         dev->set_chip_select(true);
     215           7 :         send_offset(RAMTRON_WRITE, offset);
     216           7 :         dev->transfer(buf, size, nullptr, 0);
     217           7 :         dev->set_chip_select(false);
     218             : 
     219             :         /*
     220             :           verify first 32 bytes of every write using a crc
     221             :          */
     222           7 :         uint8_t rbuf[32] {};
     223           7 :         const uint8_t nverify = MIN(size, sizeof(rbuf));
     224           7 :         uint32_t crc1 = crc_crc32(0, buf, nverify);
     225             : 
     226           7 :         dev->set_chip_select(true);
     227           7 :         send_offset(RAMTRON_READ, offset);
     228           7 :         dev->transfer(nullptr, 0, rbuf, nverify);
     229           7 :         dev->set_chip_select(false);
     230             : 
     231           7 :         uint32_t crc2 = crc_crc32(0, rbuf, nverify);
     232             : 
     233           7 :         if (crc1 == crc2) {
     234           7 :             return true;
     235             :         }
     236             :     }
     237             : 
     238           0 :     return false;
     239           7 : }

Generated by: LCOV version 1.14