Getting real FlashID

User avatar
martinayotte
Posts: 141
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: 2067
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: 141
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: 2067
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.

User avatar
loboris
Posts: 514
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: 141
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 !

colman
Posts: 37
Joined: Mon May 30, 2016 7:41 am

Re: Getting real FlashID

Postby colman » Mon May 29, 2017 4:41 am

Besides to get the Flash unique ID, I also need to make use of the 3 Security Registers of Winbond's w25Qxx chip to store some related information. I have tried using this function to read it successfully. Can you also suggest how to write to them?

Regards,
Colman.

colman
Posts: 37
Joined: Mon May 30, 2016 7:41 am

Re: Getting real FlashID

Postby colman » Mon Aug 07, 2017 8:32 am

Do anyone try this on ESP-IDF Release V2.1? In my trial, the code works under V2.0, but it reads wrong data under V2.1.
The result read back seems has shift right by one bit. For example 1101 0100 0110 0100 becomes 1110 1010 0011 0010.

Colman

colman
Posts: 37
Joined: Mon May 30, 2016 7:41 am

Re: Getting real FlashID

Postby colman » Mon Aug 07, 2017 9:16 am

I have figured out the problem. Under Release V2.0, I can run the spi clock at 40MHz, but under V2.1, I can only run it at 20MHz. When spi clock runs at 20MHz, it works again.

Colman

cmorgan
Posts: 89
Joined: Thu Aug 24, 2017 12:52 am

Re: Getting real FlashID

Postby cmorgan » Mon Oct 16, 2017 11:01 pm

Any efforts to get this folded into esp-idf proper? While I like the approach and really would like the flashid value I'm hesitant to carry around patches if they aren't in mainline.

Who is online

Users browsing this forum: s.allasia and 54 guests