Getting real FlashID

User avatar
martinayotte
Posts: 40
Joined: Fri Nov 13, 2015 4:27 pm

Getting real FlashID

Postby martinayotte » Sun Mar 19, 2017 12:29 am

I was trying to find out equivalent of ESP8266 for spi_flash_get_id() in ESP32 which doesn't exist anymore.

I've found the "SpiFlashChip g_rom_flashchip" structure in sdk/include/esp32/rom/spi_flash.h, so I've tried to access it.
It works, but funnily, it is stuffed with dummy values where the g_rom_flashchip.deviceId returns 1540ef (Windbond 2MB) when the esptool.py returns the following (GigaDevice 4MB) :

Code: Select all

Manufacturer: c8
Device: 4016

So, this SpiFlashChip structure is completely useless until the SDK been fixed with REAL values...

This bring me also similar question : why system_get_chip_id() in ESP8266 doesn't exist anymore in in ESP32 ?

Those are quite basics ...

ESP_igrr
Posts: 335
Joined: Tue Dec 01, 2015 8:37 am

Re: Getting real FlashID

Postby ESP_igrr » Sun Mar 19, 2017 4:39 am

Thanks for pointing this out, we'll add a function to get the flash chip id. Does your application require the flash chip model id, or the unique flash chip id?

Regarding the (ESP32) chip ID, our recommendation is to use the mac address factory-programmed into efuses.

User avatar
martinayotte
Posts: 40
Joined: Fri Nov 13, 2015 4:27 pm

Re: Getting real FlashID

Postby martinayotte » Sun Mar 19, 2017 1:45 pm

Thanks @ESP_iggr !

In fact, both will be nice to have !
FlashID to make it backward compatible with ESP8266, and UniqueID will be nice too for other purposes.

BTW, I was looking if I could do it my self using SPI_Common_Command(), but I didn't figured out how.
Can you provide example how the SPI_Common_Command() function is working ?

ESP_igrr
Posts: 335
Joined: Tue Dec 01, 2015 8:37 am

Re: Getting real FlashID

Postby ESP_igrr » Sun Mar 19, 2017 7:42 pm

I don't think SPI_Common_Command will work in IDF as is, because it assumes that it can use SPI1 at any time... which isn't true when flash cache is enabled. At the very least you need to use 'spi_flash_guard_start();' and 'spi_flash_guard_end();' before and after 'SPI_Common_Command'. These guard functions are static in flash_ops.c, so you can probably put the wrapper around 'SPI_Common_Command' into that file.

loboris
Posts: 26
Joined: Wed Dec 21, 2016 7:40 pm

Re: Getting real FlashID

Postby loboris » Sun Mar 19, 2017 8:07 pm

martinayotte wrote:...
BTW, I was looking if I could do it my self using SPI_Common_Command(), but I didn't figured out how.
Can you provide example how the SPI_Common_Command() function is working ?


Maybe you could use the approach from https://github.com/whitecatboard/Lua-RTOS-ESP32

You have to patch 2 files in esp-idf:
components/spi_flash/flash_ops.c

Code: Select all

--- /Users/jaumeolivepetrus/esp-idf-uniqueid/components/spi_flash/flash_ops.c   2017-03-01 13:24:17.000000000 +0100
+++ flash_ops.c   2017-03-01 11:58:26.000000000 +0100
@@ -479,6 +479,156 @@
     }
 }
 
+#include "soc/soc.h"
+
+static void IRAM_ATTR spi_master_ops(int unit, unsigned int word_size, unsigned int len, unsigned char *out, unsigned char *in) {
+   unsigned int bytes = word_size * len; // Number of bytes to write / read
+   unsigned int idx = 0;
+
+   /*
+    * SPI data buffers hardware registers are 32-bit size, so we use a
+    * transfer buffer for adapt user buffers to buffers expected by hardware, this
+    * buffer is 16-word size (64 bytes)
+    *
+    */
+   uint32_t buffer[16]; // Transfer buffer
+   uint32_t wd;         // Current word
+   unsigned int wdb;     // Current byte into current word
+
+   // This is the number of bits to transfer for current chunk
+   unsigned int bits;
+
+   bytes = word_size * len;
+   while (bytes) {
+      // Populate transfer buffer in chunks of 64 bytes
+      idx = 0;
+      bits = 0;
+      while (bytes && (idx < 16)) {
+         wd = 0;
+         wdb = 4;
+         while (bytes && wdb) {
+            wd = (wd >> 8);
+            if (out) {
+               wd |= *out << 24;
+               out++;
+            } else {
+               wd |= 0xff << 24;
+            }
+            wdb--;
+            bytes--;
+            bits += 8;
+         }
+
+         while (wdb) {
+            wd = (wd >> 8);
+            wdb--;
+         }
+
+         buffer[idx] = wd;
+         idx++;
+      }
+
+      // Wait for SPI bus ready
+      while (READ_PERI_REG(SPI_CMD_REG(unit))&SPI_USR);
+
+      // Load send buffer
+       SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(unit), SPI_USR_MOSI_DBITLEN, bits - 1, SPI_USR_MOSI_DBITLEN_S);
+       SET_PERI_REG_BITS(SPI_MISO_DLEN_REG(unit), SPI_USR_MISO_DBITLEN, bits - 1, SPI_USR_MISO_DBITLEN_S);
+
+       idx = 0;
+       while ((idx << 5) < bits) {
+          WRITE_PERI_REG((SPI_W0_REG(unit) + (idx << 2)), buffer[idx]);
+          idx++;
+       }
+
+       // Start transfer
+       SET_PERI_REG_MASK(SPI_CMD_REG(unit), SPI_USR);
+
+       // Wait for SPI bus ready
+      while (READ_PERI_REG(SPI_CMD_REG(unit))&SPI_USR);
+
+      if (in) {
+         // Read data into buffer
+         idx = 0;
+         while ((idx << 5) < bits) {
+            buffer[idx] = READ_PERI_REG((SPI_W0_REG(unit) + (idx << 2)));
+            idx++;
+         }
+
+         memcpy((void *)in, (void *)buffer, bits >> 3);
+         in += (bits >> 3);
+      }
+   }
+}
+
+void IRAM_ATTR spi_flash_send_cmd(uint32_t len, uint8_t *cmd, uint8_t *res) {
+    uint32_t prev[8];
+
+    spi_flash_disable_interrupts_caches_and_other_cpu();
+
+    // Store SPI registers
+    prev[0] = READ_PERI_REG(SPI_USER_REG(1));
+    prev[1] = READ_PERI_REG(SPI_USER1_REG(1));
+    prev[2] = READ_PERI_REG(SPI_USER2_REG(1));
+    prev[3] = READ_PERI_REG(SPI_CTRL_REG(1));
+    prev[4] = READ_PERI_REG(SPI_CTRL2_REG(1));
+    prev[5] = READ_PERI_REG(SPI_SLAVE_REG(1));
+    prev[6] = READ_PERI_REG(SPI_PIN_REG(1));
+    prev[7] = READ_PERI_REG(SPI_CLOCK_REG(1));
+
+    // Clean SPI registers
+    WRITE_PERI_REG(SPI_USER_REG(1), 0);
+    WRITE_PERI_REG(SPI_USER1_REG(1), 0);
+    WRITE_PERI_REG(SPI_USER2_REG(1), 0);
+    WRITE_PERI_REG(SPI_CTRL_REG(1), 0);
+    WRITE_PERI_REG(SPI_CTRL2_REG(1), 0);
+    WRITE_PERI_REG(SPI_SLAVE_REG(1), 0);
+    WRITE_PERI_REG(SPI_PIN_REG(1), 0);
+
+    // Set SPI mode 0
+    CLEAR_PERI_REG_MASK(SPI_PIN_REG(1),  SPI_CK_IDLE_EDGE);
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(1), SPI_CK_OUT_EDGE);
+
+    // Set bit order to MSB
+    CLEAR_PERI_REG_MASK(SPI_CTRL_REG(1), SPI_WR_BIT_ORDER | SPI_RD_BIT_ORDER);
+
+    // Enable full-duplex communication
+    SET_PERI_REG_MASK(SPI_USER_REG(1), SPI_DOUTDIN);
+
+    // Configure as master
+    WRITE_PERI_REG(SPI_USER1_REG(1), 0);
+   SET_PERI_REG_BITS(SPI_CTRL2_REG(1), SPI_MISO_DELAY_MODE, 0, SPI_MISO_DELAY_MODE_S);
+   CLEAR_PERI_REG_MASK(SPI_SLAVE_REG(1), SPI_SLAVE_MODE);
+
+    // Set clock to 1 Khz
+    WRITE_PERI_REG(SPI_CLOCK_REG(1), 0x7cf89005);
+
+    // Enable MOSI / MISO / CS
+    SET_PERI_REG_MASK(SPI_USER_REG(1), SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_MOSI | SPI_USR_MISO);
+    SET_PERI_REG_MASK(SPI_CTRL2_REG(1), ((0x4 & SPI_MISO_DELAY_NUM) << SPI_MISO_DELAY_NUM_S));
+
+    // Don't use command / address phase
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(1), SPI_USR_COMMAND);
+    SET_PERI_REG_BITS(SPI_USER2_REG(1), SPI_USR_COMMAND_BITLEN, 0, SPI_USR_COMMAND_BITLEN_S);
+
+    CLEAR_PERI_REG_MASK(SPI_USER_REG(1), SPI_USR_ADDR);
+    SET_PERI_REG_BITS(SPI_USER1_REG(1), SPI_USR_ADDR_BITLEN, 0, SPI_USR_ADDR_BITLEN_S);
+
+    spi_master_ops(1, 1, len, cmd, res);
+
+    // Restore SPI registers
+    WRITE_PERI_REG(SPI_USER_REG(1),  prev[0]);
+    WRITE_PERI_REG(SPI_USER1_REG(1), prev[1]);
+    WRITE_PERI_REG(SPI_USER2_REG(1), prev[2]);
+    WRITE_PERI_REG(SPI_CTRL_REG(1),  prev[3]);
+    WRITE_PERI_REG(SPI_CTRL2_REG(1), prev[4]);
+    WRITE_PERI_REG(SPI_SLAVE_REG(1), prev[5]);
+    WRITE_PERI_REG(SPI_PIN_REG(1),   prev[6]);
+    WRITE_PERI_REG(SPI_CLOCK_REG(1), prev[7]);
+
+    spi_flash_enable_interrupts_caches_and_other_cpu();
+}
+
 #if CONFIG_SPI_FLASH_ENABLE_COUNTERS
 
 static inline void dump_counter(spi_flash_counter_t* counter, const char* name)

and components/spi_flash/include/esp_spi_flash.h

Code: Select all

--- /Users/jaumeolivepetrus/esp-idf-uniqueid/components/spi_flash/include/esp_spi_flash.h   2017-03-01 13:24:17.000000000 +0100
+++ esp_spi_flash.h   2017-03-01 12:17:00.000000000 +0100
@@ -305,6 +305,8 @@
 
 #endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
 
+void spi_flash_send_cmd(uint32_t len, uint8_t *cmd, uint8_t *res);
+
 #ifdef __cplusplus
 }
 #endif


Then you can use this code to get flash unique ID:

Code: Select all

   uint8_t flash_unique_id[8];
   // Get flash unique id
   uint8_t command[13] = {0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
   uint8_t response[13];

   spi_flash_send_cmd(sizeof(command), command, response);
   memcpy(flash_unique_id, response + 5, sizeof(flash_unique_id));


Before updating esp-idf (git pull), you may want to revert the changes:

Code: Select all

cd $(IDF_PATH)/components/spi_flash && git checkout flash_ops.c
cd $(IDF_PATH)/components/spi_flash/include && git checkout esp_spi_flash.h


Tested and works.

User avatar
martinayotte
Posts: 40
Joined: Fri Nov 13, 2015 4:27 pm

Re: Getting real FlashID

Postby martinayotte » Mon Mar 20, 2017 6:41 pm

@loboris, thank you very much !
It is now working !

Who is online

Users browsing this forum: No registered users and 3 guests