Преглед на файлове

SDK 3.0 release (#2692)

* Rebaseline firmware to non-OS SDK version 3.0
* Note that SDK version 3.0 introduces the concept of a Flash Partition Table(PT).  This is located at Flash offset 0x10000 in our firmware build.
* The firmware is now PT aware with both LFS and SPIFFS taking their partition size and location from the PT
* A new tool `tools/nodemcu-partition.py` is now used to initialise these data and can also download LFS and SPIFFS images to these partitions.
Terry Ellison преди 5 години
родител
ревизия
9a47107920

+ 32 - 29
Makefile

@@ -5,20 +5,14 @@
 TOOLCHAIN_VERSION:=20181106.0
 
 # SDK base version, as released by Espressif
-SDK_BASE_VER:=2.2.1
-
-# no patch: SDK_VER equals SDK_BASE_VER and sdk dir depends on sdk_extracted
+SDK_BASE_VER:=3.0
 SDK_VER:=$(SDK_BASE_VER)
 SDK_DIR_DEPENDS:=sdk_extracted
 
-# with patch: SDK_VER differs from SDK_BASE_VER and sdk dir depends on sdk_patched
-#SDK_PATCH_VER:=f8f27ce
-#SDK_VER:=$(SDK_BASE_VER)-$(SDK_PATCH_VER)
-#SDK_DIR_DEPENDS:=sdk_patched
-
 SDK_FILE_VER:=$(SDK_BASE_VER)
-SDK_FILE_SHA1:=48f2242d5895823709f222bf0fffce9d525996c8
-# SDK_PATCH_SHA1:=0bc21ec77b08488f04d3e1c9d161b711d07201a8
+SDK_FILE_SHA1:=029fc23fe87e03c9852de636490b2d7b9e07f01a
+ESPTOOL_VER:=2.6
+
 # Ensure we search "our" SDK before the tool-chain's SDK (if any)
 TOP_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST))))
 SDK_REL_DIR=sdk/esp_iot_sdk_v$(SDK_VER)
@@ -109,6 +103,7 @@ else
 	CPP = $(WRAPCC) xtensa-lx106-elf-gcc -E
 	OBJCOPY = xtensa-lx106-elf-objcopy
 	FIRMWAREDIR = ../bin/
+  WGET = wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused
     UNAME_S := $(shell uname -s)
     ifeq ($(UNAME_S),Linux)
 # LINUX
@@ -128,8 +123,12 @@ else
     endif
 endif
 #############################################################
-ESPTOOL ?= ../tools/esptool.py
 
+GITHUB_TOOLCHAIN = https://github.com/jmattsson/esp-toolchains
+GITHUB_SDK       = https://github.com/espressif/ESP8266_NONOS_SDK
+GITHUB_ESPTOOL   = https://github.com/espressif/esptool
+
+ESPTOOL ?= $(TOP_DIR)/tools/toolchains/esptool.py
 
 CSRCS ?= $(wildcard *.c)
 CXXSRCS ?= $(wildcard *.cpp)
@@ -234,18 +233,16 @@ $(BINODIR)/%.bin: $(IMAGEODIR)/%.out
 all: toolchain sdk_pruned pre_build .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
 
 .PHONY: sdk_extracted
-.PHONY: sdk_patched
 .PHONY: sdk_pruned
 .PHONY: toolchain
 
 sdk_extracted: $(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER)
-sdk_patched: sdk_extracted $(TOP_DIR)/sdk/.patched-$(SDK_VER)
 sdk_pruned: $(SDK_DIR_DEPENDS) $(TOP_DIR)/sdk/.pruned-$(SDK_VER)
 
 ifeq ($(OS),Windows_NT)
 toolchain:
 else
-toolchain: $(TOP_DIR)/tools/toolchains/esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION)/bin/xtensa-lx106-elf-gcc
+toolchain: $(TOP_DIR)/tools/toolchains/esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION)/bin/xtensa-lx106-elf-gcc $(TOP_DIR)/tools/toolchains/esptool.py
 
 $(TOP_DIR)/tools/toolchains/esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION)/bin/xtensa-lx106-elf-gcc: $(TOP_DIR)/cache/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz
 	mkdir -p $(TOP_DIR)/tools/toolchains/
@@ -256,22 +253,34 @@ $(TOP_DIR)/tools/toolchains/esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION)/bin/xtensa-
 $(TOP_DIR)/cache/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz:
 	mkdir -p $(TOP_DIR)/cache
 	$(summary) WGET $(patsubst $(TOP_DIR)/%,%,$@)
-	wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused https://github.com/jmattsson/esp-toolchains/releases/download/$(PLATFORM)-$(TOOLCHAIN_VERSION)/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz -O $@ || { rm -f "$@"; exit 1; }
+	$(WGET) $(GITHUB_TOOLCHAIN)/releases/download/$(PLATFORM)-$(TOOLCHAIN_VERSION)/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz -O $@ \
+	|| { rm -f "$@"; exit 1; }
 endif
 
+$(TOP_DIR)/tools/toolchains/esptool.py: $(TOP_DIR)/cache/esptool/v$(ESPTOOL_VER).tar.gz
+	mkdir -p $(TOP_DIR)/tools/toolchains/
+	tar -C $(TOP_DIR)/tools/toolchains/ -xzf $< --strip-components=1 esptool-$(ESPTOOL_VER)/esptool.py
+	chmod +x $@
+	touch $@
+
+$(TOP_DIR)/cache/esptool/v$(ESPTOOL_VER).tar.gz:
+	mkdir -p $(TOP_DIR)/cache/esptool/
+	$(WGET) $(GITHUB_ESPTOOL)/archive/v$(ESPTOOL_VER).tar.gz -O $@ || { rm -f "$@"; exit 1; }
+
 $(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER): $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip
 	mkdir -p "$(dir $@)"
 	$(summary) UNZIP $(patsubst $(TOP_DIR)/%,%,$<)
-	(cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK-$(SDK_BASE_VER) && unzip $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK-$(SDK_BASE_VER)/lib/* ESP8266_NONOS_SDK-$(SDK_BASE_VER)/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK-$(SDK_BASE_VER)/include/* ESP8266_NONOS_SDK-$(SDK_BASE_VER)/bin/esp_init_data_default_v05.bin)
+	(cd "$(dir $@)" && \
+	 rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK-$(SDK_BASE_VER) && \
+	 unzip $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip \
+	       'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/lib/*' \
+	       'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/ld/*.v6.ld' \
+	       'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/include/*' \
+	       'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/bin/esp_init_data_default_v05.bin' \
+	)
 	mv $(dir $@)/ESP8266_NONOS_SDK-$(SDK_BASE_VER) $(dir $@)/esp_iot_sdk_v$(SDK_BASE_VER)
 	touch $@
 
-$(TOP_DIR)/sdk/.patched-$(SDK_VER): $(TOP_DIR)/cache/$(SDK_PATCH_VER).patch
-	mv $(dir $@)/esp_iot_sdk_v$(SDK_BASE_VER) $(dir $@)/esp_iot_sdk_v$(SDK_VER)
-	$(summary) APPLY $(patsubst $(TOP_DIR)/%,%,$<)
-	git apply --verbose -p1 --exclude='*VERSION' --exclude='*bin/at*' --directory=$(SDK_REL_DIR) $<
-	touch $@
-
 $(TOP_DIR)/sdk/.pruned-$(SDK_VER):
 	rm -f $(SDK_DIR)/lib/liblwip.a $(SDK_DIR)/lib/libssl.a $(SDK_DIR)/lib/libmbedtls.a
 	$(summary) PRUNE libmain.a libc.a
@@ -282,15 +291,9 @@ $(TOP_DIR)/sdk/.pruned-$(SDK_VER):
 $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip:
 	mkdir -p "$(dir $@)"
 	$(summary) WGET $(patsubst $(TOP_DIR)/%,%,$@)
-	wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused https://github.com/espressif/ESP8266_NONOS_SDK/archive/v$(SDK_FILE_VER).zip -O $@ || { rm -f "$@"; exit 1; }
+	$(WGET) $(GITHUB_SDK)/archive/v$(SDK_FILE_VER).zip -O $@ || { rm -f "$@"; exit 1; }
 	(echo "$(SDK_FILE_SHA1)  $@" | sha1sum -c -) || { rm -f "$@"; exit 1; }
 
-$(TOP_DIR)/cache/$(SDK_PATCH_VER).patch:
-	mkdir -p "$(dir $@)"
-	$(summary) WGET $(SDK_PATCH_VER).patch
-	wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused "https://github.com/espressif/ESP8266_NONOS_SDK/compare/v$(SDK_BASE_VER)...$(SDK_PATCH_VER).patch" -O $@ || { rm -f "$@"; exit 1; }
-	(echo "$(SDK_PATCH_SHA1)  $@" | sha1sum -c -) || { rm -f "$@"; exit 1; }
-
 clean:
 	$(foreach d, $(SUBDIRS), $(MAKE) -C $(d) clean;)
 	$(RM) -r $(ODIR)/$(TARGET)/$(FLAVOR)

+ 2 - 1
app/Makefile

@@ -97,7 +97,6 @@ LINKFLAGS_eagle.app.v6 = 			\
 	-T$(LD_FILE) 				\
 	-Wl,@../ld/defsym.rom			\
 	-Wl,--no-check-sections 		\
-	-Wl,--wrap=_xtos_set_exception_handler	\
 	-Wl,-static 				\
 	$(addprefix -u , $(SELECTED_MODULE_SYMS)) \
 	-Wl,--start-group 			\
@@ -116,6 +115,8 @@ LINKFLAGS_eagle.app.v6 = 			\
 	$(DEP_LIBS_eagle.app.v6) 		\
 	-Wl,--end-group 			\
 	-lm
+#	-Wl,--cref
+#	-Wl,--wrap=_xtos_set_exception_handler
 
 DEPENDS_eagle.app.v6 = 				\
                 $(LD_FILE) 			\

+ 14 - 3
app/esp-gdbstub/gdbstub.c

@@ -659,11 +659,22 @@ static void ATTR_GDBFN gdb_semihost_putchar1(char c) {
 }
 
 #if !GDBSTUB_FREERTOS
-//The OS-less SDK uses the Xtensa HAL to handle exceptions. We can use those functions to catch any
-//fatal exceptions and invoke the debugger when this happens.
+/* The non-OS SDK uses the Xtensa HAL to handle exceptions, and the SDK now establishes exception
+ * handlers for EXCCAUSE errors: ILLEGAL, INSTR_ERROR, LOAD_STORE_ERROR, PRIVILEGED, UNALIGNED,
+ * LOAD_PROHIBITED and STORE_PROHIBITED.  These handlers are established in SDK/app_main.c.
+ * LOAD_STORE_ERROR is handled by SDK/user_exceptions.o:load_non_32_wide_handler() which is a
+ * fork of our version. The remaining are handled by a static function at
+ * SDK:app+main.c:offset 0x0348.
+ *
+ * Our SDK 2 load_non_32_wide_handler chained into the gdb stub handler if the error was anything
+ * other than a L8UI, L16SI or L16UI at a flash mapped address.  However in this current
+ * implementation, we have left the Espressif handler in place and handle the other errors with
+ * the debugger.  This means that the debugger will not capture other load store errors.  I
+ * might revise this.
+ */
 static void ATTR_GDBINIT install_exceptions() {
 	int i;
-	int exno[]={EXCCAUSE_ILLEGAL, EXCCAUSE_SYSCALL, EXCCAUSE_INSTR_ERROR, EXCCAUSE_LOAD_STORE_ERROR,
+	const int exno[]={EXCCAUSE_ILLEGAL, EXCCAUSE_SYSCALL, EXCCAUSE_INSTR_ERROR, /* EXCCAUSE_LOAD_STORE_ERROR, */
 			EXCCAUSE_DIVIDE_BY_ZERO, EXCCAUSE_UNALIGNED, EXCCAUSE_INSTR_DATA_ERROR, EXCCAUSE_LOAD_STORE_DATA_ERROR,
 			EXCCAUSE_INSTR_ADDR_ERROR, EXCCAUSE_LOAD_STORE_ADDR_ERROR, EXCCAUSE_INSTR_PROHIBITED,
 			EXCCAUSE_LOAD_PROHIBITED, EXCCAUSE_STORE_PROHIBITED};

+ 13 - 14
app/include/lwip/mem.h

@@ -51,40 +51,39 @@ typedef size_t mem_size_t;
  * allow these defines to be overridden.
  */
 #ifndef MEMLEAK_DEBUG
+
 #ifndef mem_free
-#define mem_free vPortFree
+#define mem_free(s)        vPortFree(s, "", __LINE__)
 #endif
 #ifndef mem_malloc
-#define mem_malloc pvPortMalloc
+#define mem_malloc(s)      pvPortMalloc(s, "", __LINE__,false)
 #endif
 #ifndef mem_calloc
-#define mem_calloc pvPortCalloc
+#define mem_calloc(l, s)   pvPortCalloc(l, s, "", __LINE__)
 #endif
 #ifndef mem_realloc
-#define mem_realloc pvPortRealloc
+#define mem_realloc(p, s)  pvPortRealloc(p, s, "", __LINE__)
 #endif
 #ifndef mem_zalloc
-#define mem_zalloc pvPortZalloc
+#define mem_zalloc(s)     pvPortZalloc(s, "", __LINE__)
 #endif
+
 #else
+
 #ifndef mem_free
-#define mem_free(s) \
-do{\
-	const char *file = mem_debug_file;\
-    vPortFree(s, file, __LINE__);\
-}while(0)
+#define mem_free(s)      vPortFree(s, mem_debug_file, __LINE__)
 #endif
 #ifndef mem_malloc
-#define mem_malloc(s) ({const char *file = mem_debug_file; pvPortMalloc(s, file, __LINE__);})
+#define mem_malloc(s)   pvPortMalloc(s, mem_debug_file, __LINE__,false)
 #endif
 #ifndef mem_calloc
-#define mem_calloc(l, s) ({const char *file = mem_debug_file; pvPortCalloc(l, s, file, __LINE__);})
+#define mem_calloc(l, s)  pvPortCalloc(l, s, mem_debug_file, __LINE__)
 #endif
 #ifndef mem_realloc
-#define mem_realloc(p, s) ({const char *file = mem_debug_file; pvPortRealloc(p, s, file, __LINE__);})
+#define mem_realloc(p, s) pvPortRealloc(p, s, mem_debug_file, __LINE__)
 #endif
 #ifndef mem_zalloc
-#define mem_zalloc(s) ({const char *file = mem_debug_file; pvPortZalloc(s, file, __LINE__);})
+#define mem_zalloc(s)   pvPortZalloc(s, mem_debug_file, __LINE__)
 #endif
 
 #endif

+ 20 - 10
app/include/user_config.h

@@ -7,8 +7,8 @@
 // this out and enabling the explicitly size, e.g. FLASH_4M.  Valid sizes are
 // FLASH_512K, FLASH_1M, FLASH_2M, FLASH_4M, FLASH_8M, FLASH_16M.
 
-#define FLASH_AUTOSIZE
-//#define FLASH_4M
+//#define FLASH_AUTOSIZE
+#define FLASH_4M
 
 
 // The firmware now selects a baudrate of 115,200 by default, but the driver
@@ -41,11 +41,8 @@
 
 // The Lua Flash Store (LFS) allows you to store Lua code in Flash memory and
 // the Lua VMS will execute this code directly from flash without needing any
-// RAM overhead.  If you want to enable LFS then set the following define to
-// the size of the store that you need.  This can be any multiple of 4kB up to
-// a maximum 256Kb.
-
-//#define LUA_FLASH_STORE 0x10000
+// RAM overhead.  Note that you should now configure LFS directly in the 
+// System Partition Table and not at build time.
 
 
 // By default Lua executes the file init.lua at start up.  The following
@@ -71,10 +68,10 @@
 // general, limiting the size of the FS only to what your application needs
 // gives the fastest start-up and imaging times.
 
+// Note that you should now configure SPIFFS size and position directly in the 
+// System Partition Table and not at build time.
+
 #define BUILD_SPIFFS
-//#define SPIFFS_FIXED_LOCATION        0x100000
-//#define SPIFFS_MAX_FILESYSTEM_SIZE    0x20000
-//#define SPIFFS_SIZE_1M_BOUNDARY
 #define SPIFFS_CACHE 1          // Enable if you use you SPIFFS in R/W mode
 #define SPIFFS_MAX_OPEN_FILES 4 // maximum number of open files for SPIFFS
 #define FS_OBJ_NAME_LEN 31      // maximum length of a filename
@@ -206,6 +203,19 @@
 // change this if you have tracked the implications through the Firmware sources
 // and understand the these.
 
+#define NODEMCU_EAGLEROM_PARTITION        1
+#define NODEMCU_IROM0TEXT_PARTITION       2
+#define NODEMCU_LFS0_PARTITION            3
+#define NODEMCU_LFS1_PARTITION            4
+#define NODEMCU_TLSCERT_PARTITION         5
+#define NODEMCU_SPIFFS0_PARTITION         6
+#define NODEMCU_SPIFFS1_PARTITION         7
+
+#define LUA_FLASH_STORE                   0x0
+#define SPIFFS_FIXED_LOCATION             0x0
+#define SPIFFS_MAX_FILESYSTEM_SIZE        (~0x0)
+//#define SPIFFS_SIZE_1M_BOUNDARY
+
 #define LUA_TASK_PRIO             USER_TASK_PRIO_0
 #define LUA_PROCESS_LINE_SIG      2
 #define LUA_OPTIMIZE_DEBUG        2

+ 15 - 14
app/libc/c_math.c

@@ -11,7 +11,7 @@ double floor(double x)
 #define MINEXP -2047        /* (MIN_EXP * 16) - 1           */
 #define HUGE MAXFLOAT
 
-double a1[] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR =
+static const double a1[] =
 {
     1.0,
     0.95760328069857365,
@@ -31,7 +31,7 @@ double a1[] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR =
     0.52213689121370692,
     0.50000000000000000
 };
-double a2[] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR =
+static const double a2[] =
 {
     0.24114209503420288E-17,
     0.92291566937243079E-18,
@@ -42,18 +42,19 @@ double a2[] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR =
     0.29306999570789681E-17,
     0.11260851040933474E-17
 };
-double p1 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.833333333333332114e-1;
-double p2 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.125000000005037992e-1;
-double p3 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.223214212859242590e-2;
-double p4 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.434457756721631196e-3;
-double q1 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.693147180559945296e0;
-double q2 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.240226506959095371e0;
-double q3 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.555041086640855953e-1;
-double q4 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.961812905951724170e-2;
-double q5 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.133335413135857847e-2;
-double q6 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.154002904409897646e-3;
-double q7 ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.149288526805956082e-4;
-double k ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = 0.442695040888963407;
+static const double
+    p1 = 0.833333333333332114e-1,
+    p2 = 0.125000000005037992e-1,
+    p3 = 0.223214212859242590e-2,
+    p4 = 0.434457756721631196e-3,
+    q1 = 0.693147180559945296e0,
+    q2 = 0.240226506959095371e0,
+    q3 = 0.555041086640855953e-1,
+    q4 = 0.961812905951724170e-2,
+    q5 = 0.133335413135857847e-2,
+    q6 = 0.154002904409897646e-3,
+    q7 = 0.149288526805956082e-4,
+    k  = 0.442695040888963407;
 
 double pow(double x, double y)
 {

+ 3 - 3
app/lua/ldblib.c

@@ -28,12 +28,12 @@ static int db_getregistry (lua_State *L) {
 }
 
 static int db_getstrings (lua_State *L) {
-  size_t i,n;
+  size_t i,n=0;
   stringtable *tb;
   GCObject *o;
-#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER)
+#ifndef LUA_CROSS_COMPILER
   const char *opt = lua_tolstring (L, 1, &n);
-  if (n==3 && memcmp(opt, "ROM", 4) == 0) {
+  if (n==3 && c_memcmp(opt, "ROM", 4) == 0) {
     if (G(L)->ROstrt.hash == NULL)
       return 0;
     tb = &G(L)->ROstrt;

+ 29 - 16
app/lua/lflash.c

@@ -8,7 +8,6 @@
 #define LUAC_CROSS_FILE
 #include "lua.h"
 
-#ifdef LUA_FLASH_STORE
 #include "lobject.h"
 #include "lauxlib.h"
 #include "lstate.h"
@@ -31,6 +30,7 @@
  */
 
 static char    *flashAddr;
+static uint32_t flashSize;
 static uint32_t flashAddrPhys;
 static uint32_t flashSector;
 static uint32_t curOffset;
@@ -38,9 +38,8 @@ static uint32_t curOffset;
 #define ALIGN(s)      (((s)+sizeof(size_t)-1) & ((size_t) (- (signed) sizeof(size_t))))
 #define ALIGN_BITS(s) (((uint32_t)s) & (sizeof(size_t)-1))
 #define ALL_SET       (~0)
-#define FLASH_SIZE    LUA_FLASH_STORE
 #define FLASH_PAGE_SIZE INTERNAL_FLASH_SECTOR_SIZE
-#define FLASH_PAGES   (FLASH_SIZE/FLASH_PAGE_SIZE)
+#define FLASH_PAGES   (flashSize/FLASH_PAGE_SIZE)
 #define READ_BLOCKSIZE      1024
 #define WRITE_BLOCKSIZE     2048
 #define DICTIONARY_WINDOW  16384
@@ -49,8 +48,6 @@ static uint32_t curOffset;
 #define WRITE_BLOCKS       ((DICTIONARY_WINDOW/WRITE_BLOCKSIZE)+1)
 #define WRITE_BLOCK_WORDS  (WRITE_BLOCKSIZE/WORDSIZE)
 
-char flash_region_base[FLASH_SIZE] ICACHE_FLASH_RESERVED_ATTR;
-
 struct INPUT {
   int      fd;
   int      len;
@@ -152,11 +149,16 @@ static void flashErase(uint32_t start, uint32_t end){
  * Hook in lstate.c:f_luaopen() to set up ROstrt and ROpvmain if needed
  */
 LUAI_FUNC void luaN_init (lua_State *L) {
-  curOffset       = 0;
-  flashAddr       = flash_region_base;
-  flashAddrPhys   = platform_flash_mapped2phys((uint32_t)flashAddr);
+
+  flashSize = platform_flash_get_partition (NODEMCU_LFS0_PARTITION, &flashAddrPhys);
+  if (flashSize == 0) {
+    return;   // Nothing to do if the size is zero
+  }
+  G(L)->LFSsize   = flashSize;
+  flashAddr       = cast(char *, platform_flash_phys2mapped(flashAddrPhys));
   flashSector     = platform_flash_get_sector_of_address(flashAddrPhys);
   FlashHeader *fh = cast(FlashHeader *, flashAddr);
+  curOffset       = 0;
 
   /*
    * For the LFS to be valid, its signature has to be correct for this build
@@ -201,6 +203,13 @@ LUALIB_API int luaN_reload_reboot (lua_State *L) {
   // luaL_dbgbreak();
   const char *fn = lua_tostring(L, 1), *msg = "";
   int status;
+
+  if (G(L)->LFSsize == 0) {
+    lua_pushstring(L, "No LFS partition allocated");
+    return 1;
+  }
+
+
  /*
   * Do a protected call of loadLFS.
   *
@@ -264,13 +273,18 @@ LUAI_FUNC int luaN_index (lua_State *L) {
   int i;
   int n = lua_gettop(L);
 
-  /* Return nil + the LFS base address if the LFS isn't loaded */
-  if(!(G(L)->ROpvmain)) {
+  /* Return nil + the LFS base address if the LFS size > 0 and it isn't loaded */
+  if (!(G(L)->ROpvmain)) {
     lua_settop(L, 0);
     lua_pushnil(L);
-    lua_pushinteger(L, (lua_Integer) flashAddr);
-    lua_pushinteger(L, flashAddrPhys);
-    return 3;
+    if (G(L)->LFSsize) {
+      lua_pushinteger(L, (lua_Integer) flashAddr);
+      lua_pushinteger(L, flashAddrPhys);
+      lua_pushinteger(L, G(L)->LFSsize);
+      return 4;
+    } else {
+      return 1;
+    }
   }
 
   /* Push the LClosure of the LFS index function */
@@ -409,10 +423,10 @@ int procFirstPass (void) {
       flash_error("Incorrect LFS build type");
     if ((fh->flash_sig & ~FLASH_SIG_ABSOLUTE) != FLASH_SIG)
       flash_error("incorrect LFS header signature");
-    if (fh->flash_size > FLASH_SIZE)
+    if (fh->flash_size > flashSize)
       flash_error("LFS Image too big for configured LFS region");
     if ((fh->flash_size & 0x3) ||
-         fh->flash_size > FLASH_SIZE ||
+         fh->flash_size > flashSize ||
          out->flagsLen != 1 + (out->flashLen/WORDSIZE - 1) / BITS_PER_WORD)
       flash_error("LFS length mismatch");
     out->flags = luaM_newvector(out->L, out->flagsLen, uint);
@@ -557,4 +571,3 @@ static int loadLFSgc (lua_State *L) {
   }
   return 0;
 }
-#endif

+ 1 - 1
app/lua/lflash.h

@@ -3,7 +3,7 @@
 ** See Copyright Notice in lua.h
 */
 
-#if defined(LUA_FLASH_STORE) && !defined(lflash_h)
+#ifndef lflash_h
 #define lflash_h
 
 #include "lobject.h"

+ 0 - 4
app/lua/lgc.c

@@ -28,10 +28,6 @@
 #define GCSWEEPCOST	10
 #define GCFINALIZECOST	100
 
-#if READONLYMASK != (1<<READONLYBIT) || (defined(LUA_FLASH_STORE) && LFSMASK  != (1<<LFSBIT))
-#error "lgc.h and object.h out of sync on READONLYMASK / LFSMASK"
-#endif
-
 #define maskmarks	cast_byte(~(bitmask(BLACKBIT)|WHITEBITS))
 
 #define makewhite(g,x)	\

+ 1 - 1
app/lua/lgc.h

@@ -101,7 +101,7 @@
 #define isfixedstack(x)	testbit((x)->marked, FIXEDSTACKBIT)
 #define fixedstack(x)	l_setbit((x)->marked, FIXEDSTACKBIT)
 #define unfixedstack(x)	resetbit((x)->marked, FIXEDSTACKBIT)
-#ifdef LUA_FLASH_STORE
+#ifndef LUA_CROSS_COMPILER
 #define isLFSobject(x)  testbit(getmarked(x), LFSBIT)
 #define stringfix(s)    if (!test2bits(getmarked(&(s)->tsv), FIXEDBIT, LFSBIT)) {l_setbit((s)->tsv.marked, FIXEDBIT);}
 #else

+ 0 - 2
app/lua/lobject.h

@@ -24,9 +24,7 @@
 #define NUM_TAGS	(LAST_TAG+1)
 
 #define READONLYMASK    (1<<7)      /* denormalised bitmask for READONLYBIT and */
-#ifdef LUA_FLASH_STORE
 #define LFSMASK         (1<<6)      /* LFSBIT to avoid include proliferation */
-#endif
 /*
 ** Extra tags for non-values
 */

+ 3 - 2
app/lua/lstate.c

@@ -73,7 +73,7 @@ static void f_luaopen (lua_State *L, void *ud) {
   sethvalue(L, gt(L), luaH_new(L, 0, 2));  /* table of globals */
   sethvalue(L, registry(L), luaH_new(L, 0, 2));  /* registry */
   luaS_resize(L, MINSTRTABSIZE);  /* initial size of string table */
-#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER)
+#ifndef LUA_CROSS_COMPILER
   luaN_init(L);                   /* optionally map RO string table */
 #endif
   luaT_init(L);
@@ -196,11 +196,12 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
 #else
   g->memlimit = 0;
 #endif
-#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER)
+#ifndef LUA_CROSS_COMPILER
   g->ROstrt.size = 0;
   g->ROstrt.nuse = 0;
   g->ROstrt.hash = NULL;
   g->ROpvmain    = NULL;
+  g->LFSsize     = 0;
 #endif
   for (i=0; i<NUM_TAGS; i++) g->mt[i] = NULL;
   if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {

+ 2 - 1
app/lua/lstate.h

@@ -94,9 +94,10 @@ typedef struct global_State {
   UpVal uvhead;  /* head of double-linked list of all open upvalues */
   struct Table *mt[NUM_TAGS];  /* metatables for basic types */
   TString *tmname[TM_N];  /* array with tag-method names */
-#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER)
+#ifndef LUA_CROSS_COMPILER
   stringtable ROstrt;  /* Flash-based hash table for RO strings */
   Proto *ROpvmain;   /* Flash-based Proto main */
+  int LFSsize;  /* Size of Lua Flash Store */
 #endif
 } global_State;
 

+ 1 - 1
app/lua/lstring.c

@@ -112,7 +112,7 @@ LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
       return ts;
     }
   }
-#if defined(LUA_FLASH_STORE) && !defined(LUA_CROSS_COMPILER)
+#ifndef LUA_CROSS_COMPILER
   /*
    * The RAM strt is searched first since RAM access is faster tham Flash access.
    * If a miss, then search the RO string table.

+ 0 - 2
app/lua/lua.c

@@ -22,9 +22,7 @@
 #include "lauxlib.h"
 #include "lualib.h"
 #include "legc.h"
-#ifdef LUA_FLASH_STORE
 #include "lflash.h"
-#endif
 #include "os_type.h"
 
 lua_State *globalL = NULL;

+ 0 - 2
app/lua/luac_cross/lflashimg.c

@@ -16,8 +16,6 @@
 #define LUA_CORE
 #include "lobject.h"
 #include "lstring.h"
-#undef LUA_FLASH_STORE
-#define LUA_FLASH_STORE
 #include "lflash.h"
 #include "uzlib.h"
 

+ 0 - 8
app/modules/node.c

@@ -17,9 +17,7 @@
 
 #include "platform.h"
 #include "lrodefs.h"
-#ifdef LUA_FLASH_STORE
 #include "lflash.h"
-#endif
 #include "c_types.h"
 #include "c_string.h"
 #include "driver/uart.h"
@@ -162,10 +160,6 @@ static int node_flashid( lua_State* L )
 // Lua: flashsize()
 static int node_flashsize( lua_State* L )
 {
-  if (lua_type(L, 1) == LUA_TNUMBER)
-  {
-    flash_rom_set_size_byte(luaL_checkinteger(L, 1));
-  }
   uint32_t sz = flash_rom_get_size_byte();
   lua_pushinteger( L, sz );
   return 1;
@@ -603,10 +597,8 @@ static const LUA_REG_TYPE node_map[] =
   { LSTRKEY( "heap" ), LFUNCVAL( node_heap ) },
   { LSTRKEY( "info" ), LFUNCVAL( node_info ) },
   { LSTRKEY( "task" ), LROVAL( node_task_map ) },
-#ifdef LUA_FLASH_STORE
   { LSTRKEY( "flashreload" ), LFUNCVAL( luaN_reload_reboot ) },
   { LSTRKEY( "flashindex" ), LFUNCVAL( luaN_index ) },
-#endif
   { LSTRKEY( "restart" ),   LFUNCVAL( node_restart ) },
   { LSTRKEY( "dsleep" ),    LFUNCVAL( node_deepsleep ) },
   { LSTRKEY( "dsleepMax" ), LFUNCVAL( dsleepMax ) },

+ 2 - 2
app/modules/rtctime.c

@@ -46,10 +46,10 @@ void __attribute__((noreturn)) TEXT_SECTION_ATTR rtc_time_enter_deep_sleep_final
 
 void rtctime_early_startup (void)
 {
-  Cache_Read_Enable (0, 0, 1);
+//  Cache_Read_Enable (0, 0, 1);
   rtc_time_register_bootup ();
   rtc_time_switch_clocks ();
-  Cache_Read_Disable ();
+//  Cache_Read_Disable ();
 }
 
 void rtctime_late_startup (void)

+ 14 - 162
app/platform/flash_api.c

@@ -39,16 +39,19 @@ uint32_t flash_detect_size_byte(void)
 #undef FLASH_BUFFER_SIZE_DETECT
 }
 
-SPIFlashInfo flash_rom_getinfo(void)
+static SPIFlashInfo spi_flash_info = {0};
+
+SPIFlashInfo *flash_rom_getinfo(void)
 {
-    volatile SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR;
-    spi_flash_read(0, (uint32 *)(& spi_flash_info), sizeof(spi_flash_info));
-    return spi_flash_info;
+    if (spi_flash_info.entry_point == 0) {
+        spi_flash_read(0, (uint32 *)(& spi_flash_info), sizeof(spi_flash_info));
+    }
+    return &spi_flash_info;
 }
 
 uint8_t flash_rom_get_size_type(void)
 {
-    return flash_rom_getinfo().size;
+    return flash_rom_getinfo()->size;
 }
 
 uint32_t flash_rom_get_size_byte(void)
@@ -56,7 +59,7 @@ uint32_t flash_rom_get_size_byte(void)
     static uint32_t flash_size = 0;
     if (flash_size == 0)
     {
-        switch (flash_rom_getinfo().size)
+        switch (flash_rom_getinfo()->size)
         {
         case SIZE_2MBIT:
             // 2Mbit, 256kByte
@@ -107,99 +110,15 @@ uint32_t flash_rom_get_size_byte(void)
     return flash_size;
 }
 
-bool flash_rom_set_size_type(uint8_t size)
-{
-    // Dangerous, here are dinosaur infested!!!!!
-    // Reboot required!!!
-    // If you don't know what you're doing, your nodemcu may turn into stone ...
-    NODE_DBG("\nBEGIN SET FLASH HEADER\n");
-    uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR;
-    if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE))
-    {
-        NODE_DBG("\nflash_rom_set_size_type(%u), was %u\n", size, ((SPIFlashInfo *)data)->size );
-        ((SPIFlashInfo *)data)->size = size;
-        if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(0 * SPI_FLASH_SEC_SIZE))
-        {
-            NODE_DBG("\nSECTOR 0 ERASE SUCCESS\n");
-        }
-        if (SPI_FLASH_RESULT_OK == spi_flash_write(0, (uint32 *)data, SPI_FLASH_SEC_SIZE))
-        {
-            NODE_DBG("\nWRITE SUCCESS, %u\n", size);
-        }
-    }
-    NODE_DBG("\nEND SET FLASH HEADER\n");
-    return true;
-}
-
-bool flash_rom_set_size_byte(uint32_t size)
-{
-    // Dangerous, here are dinosaur infested!!!!!
-    // Reboot required!!!
-    // If you don't know what you're doing, your nodemcu may turn into stone ...
-    bool result = true;
-    uint32_t flash_size = 0;
-    switch (size)
-    {
-    case 256 * 1024:
-        // 2Mbit, 256kByte
-        flash_size = SIZE_2MBIT;
-        flash_rom_set_size_type(flash_size);
-        break;
-    case 512 * 1024:
-        // 4Mbit, 512kByte
-        flash_size = SIZE_4MBIT;
-        flash_rom_set_size_type(flash_size);
-        break;
-    case 1 * 1024 * 1024:
-        // 8Mbit, 1MByte
-        flash_size = SIZE_8MBIT;
-        flash_rom_set_size_type(flash_size);
-        break;
-    case 2 * 1024 * 1024:
-        // 16Mbit, 2MByte
-        flash_size = SIZE_16MBIT;
-        flash_rom_set_size_type(flash_size);
-        break;
-    case 4 * 1024 * 1024:
-        // 32Mbit, 4MByte
-        flash_size = SIZE_32MBIT;
-        flash_rom_set_size_type(flash_size);
-        break;
-    case 8 * 1024 * 1024:
-        // 64Mbit, 8MByte
-        flash_size = SIZE_64MBIT;
-        flash_rom_set_size_type(flash_size);
-        break;
-    case 16 * 1024 * 1024:
-        // 128Mbit, 16MByte
-        flash_size = SIZE_128MBIT;
-        flash_rom_set_size_type(flash_size);
-        break;
-    default:
-        // Unknown flash size.
-        result = false;
-        break;
-    }
-    return result;
-}
-
 uint16_t flash_rom_get_sec_num(void)
 {
-    //static uint16_t sec_num = 0;
-    // return flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE);
-    // c_printf("\nflash_rom_get_size_byte()=%d\n", ( flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE) ));
-    // if( sec_num == 0 )
-    //{
-    //    sec_num = 4 * 1024 * 1024 / (SPI_FLASH_SEC_SIZE);
-    //}
-    //return sec_num;
     return ( flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE) );
 }
 
 uint8_t flash_rom_get_mode(void)
 {
-    SPIFlashInfo spi_flash_info = flash_rom_getinfo();
-    switch (spi_flash_info.mode)
+    uint8_t mode = flash_rom_getinfo()->mode;
+    switch (mode)
     {
     // Reserved for future use
     case MODE_QIO:
@@ -211,14 +130,14 @@ uint8_t flash_rom_get_mode(void)
     case MODE_DOUT:
         break;
     }
-    return spi_flash_info.mode;
+    return mode;
 }
 
 uint32_t flash_rom_get_speed(void)
 {
     uint32_t speed = 0;
-    SPIFlashInfo spi_flash_info = flash_rom_getinfo();
-    switch (spi_flash_info.speed)
+    uint8_t spi_speed = flash_rom_getinfo()->speed;
+    switch (spi_speed)
     {
     case SPEED_40MHZ:
         // 40MHz
@@ -240,47 +159,6 @@ uint32_t flash_rom_get_speed(void)
     return speed;
 }
 
-bool flash_rom_set_speed(uint32_t speed)
-{
-    // Dangerous, here are dinosaur infested!!!!!
-    // Reboot required!!!
-    // If you don't know what you're doing, your nodemcu may turn into stone ...
-    NODE_DBG("\nBEGIN SET FLASH HEADER\n");
-    uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR;
-    uint8_t speed_type = SPEED_40MHZ;
-    if (speed < 26700000)
-    {
-        speed_type = SPEED_20MHZ;
-    }
-    else if (speed < 40000000)
-    {
-        speed_type = SPEED_26MHZ;
-    }
-    else if (speed < 80000000)
-    {
-        speed_type = SPEED_40MHZ;
-    }
-    else if (speed >= 80000000)
-    {
-        speed_type = SPEED_80MHZ;
-    }
-    if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE))
-    {
-        ((SPIFlashInfo *)(&data[0]))->speed = speed_type;
-        NODE_DBG("\nflash_rom_set_speed(%u), was %u\n", speed_type, ((SPIFlashInfo *)(&data[0]))->speed );
-        if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(0 * SPI_FLASH_SEC_SIZE))
-        {
-            NODE_DBG("\nERASE SUCCESS\n");
-        }
-        if (SPI_FLASH_RESULT_OK == spi_flash_write(0, (uint32 *)data, SPI_FLASH_SEC_SIZE))
-        {
-            NODE_DBG("\nWRITE SUCCESS, %u\n", speed_type);
-        }
-    }
-    NODE_DBG("\nEND SET FLASH HEADER\n");
-    return true;
-}
-
 uint8_t byte_of_aligned_array(const uint8_t *aligned_array, uint32_t index)
 {
     if ( (((uint32_t)aligned_array) % 4) != 0 )
@@ -303,31 +181,5 @@ uint16_t word_of_aligned_array(const uint16_t *aligned_array, uint32_t index)
     volatile uint32_t v = ((uint32_t *)aligned_array)[ index / 2 ];
     uint16_t *p = (uint16_t *) (&v);
     return (index % 2 == 0) ? p[ 0 ] : p[ 1 ];
-    // return p[ (index % 2) ]; // -- why error???
-    // (byte_of_aligned_array((uint8_t *)aligned_array, index * 2 + 1) << 8) | byte_of_aligned_array((uint8_t *)aligned_array, index * 2);
 }
 
-// uint8_t flash_rom_get_checksum(void)
-// {
-//     // SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR = flash_rom_getinfo();
-//     // uint32_t address = sizeof(spi_flash_info) + spi_flash_info.segment_size;
-//     // uint32_t address_aligned_4bytes = (address + 3) & 0xFFFFFFFC;
-//     // uint8_t buffer[64] = {0};
-//     // spi_flash_read(address, (uint32 *) buffer, 64);
-//     // uint8_t i = 0;
-//     // c_printf("\nBEGIN DUMP\n");
-//     // for (i = 0; i < 64; i++)
-//     // {
-//     //     c_printf("%02x," , buffer[i]);
-//     // }
-//     // i = (address + 0x10) & 0x10 - 1;
-//     // c_printf("\nSIZE:%d CHECK SUM:%02x\n", spi_flash_info.segment_size, buffer[i]);
-//     // c_printf("\nEND DUMP\n");
-//     // return buffer[0];
-//     return 0;
-// }
-
-// uint8_t flash_rom_calc_checksum(void)
-// {
-//     return 0;
-// }

+ 1 - 5
app/platform/flash_api.h

@@ -79,18 +79,14 @@ typedef struct
     uint32_t segment_size;
 } ICACHE_STORE_TYPEDEF_ATTR SPIFlashInfo;
 
-SPIFlashInfo flash_rom_getinfo(void);
+SPIFlashInfo *flash_rom_getinfo(void);
 uint8_t flash_rom_get_size_type(void);
 uint32_t flash_rom_get_size_byte(void);
 uint32_t flash_detect_size_byte(void);
-bool flash_rom_set_size_type(uint8_t);
-bool flash_rom_set_size_byte(uint32_t);
 uint16_t flash_rom_get_sec_num(void);
 uint8_t flash_rom_get_mode(void);
 uint32_t flash_rom_get_speed(void);
 uint8_t byte_of_aligned_array(const uint8_t* aligned_array, uint32_t index);
 uint16_t word_of_aligned_array(const uint16_t *aligned_array, uint32_t index);
-// uint8_t flash_rom_get_checksum(void);
-// uint8_t flash_rom_calc_checksum(void);
 
 #endif // __FLASH_API_H__

+ 9 - 0
app/platform/platform.c

@@ -946,6 +946,15 @@ uint32_t platform_flash_phys2mapped (uint32_t phys_addr) {
   return (meg&1) ? -1 : phys_addr + INTERNAL_FLASH_MAPPED_ADDRESS - meg;
 }
 
+uint32_t platform_flash_get_partition (uint32_t part_id, uint32_t *addr)  {
+  partition_item_t pt = {0,0,0};
+  system_partition_get_item(SYSTEM_PARTITION_CUSTOMER_BEGIN + part_id, &pt);
+  if (addr) {
+    *addr = pt.addr;
+  }
+  return  pt.type == 0 ? 0 : pt.size;
+}
+
 void* platform_print_deprecation_note( const char *msg, const char *time_frame)
 {
   c_printf( "Warning, deprecated API! %s. It will be removed %s. See documentation for details.\n", msg, time_frame );

+ 1 - 0
app/platform/platform.h

@@ -289,6 +289,7 @@ int platform_flash_erase_sector( uint32_t sector_id );
  */
 uint32_t platform_flash_mapped2phys (uint32_t mapped_addr);
 uint32_t platform_flash_phys2mapped (uint32_t phys_addr);
+uint32_t platform_flash_get_partition (uint32_t part_id, uint32_t *addr);
 
 // *****************************************************************************
 // Allocator support

+ 7 - 4
app/platform/vfs.c

@@ -33,11 +33,14 @@ sint32_t vfs_get_rtc( vfs_time *tm )
 
 static int dir_level = 1;
 
-static const char *normalize_path( const char *path )
-{
+
 #if ! LDRV_TRAVERSAL
-  return path;
+
+#define normalize_path(p) (p)
+
 #else
+static const char *normalize_path( const char *path )
+{
   const char *temp = path;
   size_t len;
 
@@ -63,8 +66,8 @@ static const char *normalize_path( const char *path )
     // path traverses via root
     return temp;
   }
-#endif
 }
+#endif
 
 
 // ---------------------------------------------------------------------------

+ 1 - 0
app/spiffs/Makefile

@@ -23,6 +23,7 @@ endif
 #   for a subtree within the makefile rooted therein
 #
 DEFINES += -Dprintf=c_printf
+#DEFINES += -DDEVELOPMENT_TOOLS -DNODE_DEBUG -DSPIFFS_API_DBG=NODE_DBG
 
 #############################################################
 # Recursion Magic - Don't touch this!!

+ 0 - 20
app/spiffs/myspiffs.h

@@ -1,20 +0,0 @@
-#include "spiffs.h"
-bool myspiffs_mount();
-void myspiffs_unmount();
-int myspiffs_open(const char *name, int flags);
-int myspiffs_close( int fd );
-size_t myspiffs_write( int fd, const void* ptr, size_t len );
-size_t myspiffs_read( int fd, void* ptr, size_t len);
-int myspiffs_lseek( int fd, int off, int whence );
-int myspiffs_eof( int fd );
-int myspiffs_tell( int fd );
-int myspiffs_getc( int fd );
-int myspiffs_ungetc( int c, int fd );
-int myspiffs_flush( int fd );
-int myspiffs_error( int fd );
-void myspiffs_clearerr( int fd );
-int myspiffs_check( void );
-int myspiffs_rename( const char *old, const char *newname );
-size_t myspiffs_size( int fd );
-int myspiffs_format (void);
-

+ 61 - 115
app/spiffs/spiffs.c

@@ -2,14 +2,31 @@
 #include "platform.h"
 #include "spiffs.h"
 
+/*
+ * With the intoduction of a unified FatFS and SPIFFS support (#1397), the SPIFFS
+ * interface is now abstracted through a uses a single SPIFFS entry point
+ * myspiffs_realm() which returns a vfs_fs_fns object (as does myfatfs_realm()).
+ * All other functions and data are static.
+ *
+ * Non-OS SDK V3.0 introduces a flash partition table (PT) and SPIFFS has now been
+ * updated to support this:
+ *   -  SPIFFS limits search to the specifed SPIFFS0 address and size.
+ *   -  Any headroom / offset from other partitions is reflected in the PT allocations.
+ *   -  Unforced mounts will attempt to mount any valid SPIFSS found in this range
+ *      (NodeMCU uses the SPIFFS_USE_MAGIC setting to make existing FS discoverable).
+ *   -  Subject to the following, no offset or FS search is done.  The FS is assumed
+ *      to be at the first valid location at the start of the partition.
+ */
 #include "spiffs_nucleus.h"
 
-spiffs fs;
+static spiffs fs;
 
 #define LOG_PAGE_SIZE       	256
 #define LOG_BLOCK_SIZE		(INTERNAL_FLASH_SECTOR_SIZE * 2)
 #define LOG_BLOCK_SIZE_SMALL_FS	(INTERNAL_FLASH_SECTOR_SIZE)
 #define MIN_BLOCKS_FS		4
+#define MASK_1MB (0x100000-1)
+#define ALIGN (0x2000)
 
 static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
 static u8_t spiffs_fds[sizeof(spiffs_fd) * SPIFFS_MAX_OPEN_FILES];
@@ -42,131 +59,64 @@ void myspiffs_check_callback(spiffs_check_type type, spiffs_check_report report,
 }
 
 /*******************
-The W25Q32BV array is organized into 16,384 programmable pages of 256-bytes each. Up to 256 bytes can be programmed at a time. 
-Pages can be erased in groups of 16 (4KB sector erase), groups of 128 (32KB block erase), groups of 256 (64KB block erase) or 
-the entire chip (chip erase). The W25Q32BV has 1,024 erasable sectors and 64 erasable blocks respectively. 
-The small 4KB sectors allow for greater flexibility in applications that require data and parameter storage. 
-
-********************/
+ * Note that the W25Q32BV array is organized into 16,384 programmable pages of 256-bytes 
+ * each. Up to 256 bytes can be programmed at a time.  Pages can be erased in groups of 
+ * 16 (4KB sector erase), groups of 128 (32KB block erase), groups of 256 (64KB block 
+ * erase) or the entire chip (chip erase). The W25Q32BV has 1,024 erasable sectors and 
+ * 64 erasable blocks respectively. The small 4KB sectors allow for greater flexibility 
+ * in applications that require data and parameter storage. 
+ *
+ * Returns  TRUE if FS was found.
+ */
+static bool myspiffs_set_cfg(spiffs_config *cfg, bool force_create) {
+  uint32 pt_start, pt_size, pt_end;
 
-static bool myspiffs_set_location(spiffs_config *cfg, int align, int offset, int block_size) {
-#ifdef SPIFFS_FIXED_LOCATION
-  cfg->phys_addr = (SPIFFS_FIXED_LOCATION + block_size - 1) & ~(block_size-1);
-#else
-  cfg->phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ) + offset;
-  cfg->phys_addr = (cfg->phys_addr + align - 1) & ~(align - 1);
-#endif
-#ifdef SPIFFS_SIZE_1M_BOUNDARY
-  cfg->phys_size = ((0x100000 - (SYS_PARAM_SEC_NUM * INTERNAL_FLASH_SECTOR_SIZE) - ( ( u32_t )cfg->phys_addr )) & ~(block_size - 1)) & 0xfffff;
-#else
-  cfg->phys_size = (INTERNAL_FLASH_SIZE - ( ( u32_t )cfg->phys_addr )) & ~(block_size - 1);
-#endif
-  if ((int) cfg->phys_size < 0) {
+  pt_size = platform_flash_get_partition (NODEMCU_SPIFFS0_PARTITION, &pt_start);
+  if (pt_size == 0) {
     return FALSE;
   }
-  cfg->log_block_size = block_size;
-
-  return (cfg->phys_size / block_size) >= MIN_BLOCKS_FS;
-}
-
-/*
- * Returns  TRUE if FS was found
- * align must be a power of two
- */
-static bool myspiffs_set_cfg_block(spiffs_config *cfg, int align, int offset, int block_size, bool force_create) {
-  cfg->phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet
-  cfg->log_page_size = LOG_PAGE_SIZE; // as we said
+  pt_end = pt_start + pt_size;
 
   cfg->hal_read_f = my_spiffs_read;
   cfg->hal_write_f = my_spiffs_write;
   cfg->hal_erase_f = my_spiffs_erase;
-
-  if (!myspiffs_set_location(cfg, align, offset, block_size)) {
+  cfg->phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE;
+  cfg->log_page_size = LOG_PAGE_SIZE;
+  cfg->phys_addr = (pt_start + ALIGN - 1) & ~(ALIGN - 1);
+  cfg->phys_size = (pt_end & ~(ALIGN - 1)) - cfg->phys_addr;
+  
+  if (cfg->phys_size < MIN_BLOCKS_FS * LOG_BLOCK_SIZE_SMALL_FS) {
     return FALSE;
+  } else if (cfg->phys_size < MIN_BLOCKS_FS * LOG_BLOCK_SIZE_SMALL_FS) {
+    cfg->log_block_size = LOG_BLOCK_SIZE_SMALL_FS;
+  } else  {
+    cfg->log_block_size = LOG_BLOCK_SIZE;
   }
 
-  NODE_DBG("fs.start:%x,max:%x\n",cfg->phys_addr,cfg->phys_size);
-
 #ifdef SPIFFS_USE_MAGIC_LENGTH
-  if (force_create) {
-    return TRUE;
-  }
-
-  int size = SPIFFS_probe_fs(cfg);
-
-  if (size > 0 && size < cfg->phys_size) {
-    NODE_DBG("Overriding size:%x\n",size);
-    cfg->phys_size = size;
-  }
-  if (size > 0) {
-    return TRUE;
-  }
-  return FALSE;
-#else
-  return TRUE;
-#endif
-}
-
-static bool myspiffs_set_cfg(spiffs_config *cfg, int align, int offset, bool force_create) {
-  if (force_create) {
-    return myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE         , TRUE) ||
-           myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE_SMALL_FS, TRUE);
-  }
-
-  return myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE_SMALL_FS, FALSE) ||
-         myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE         , FALSE);
-}
-
-static bool myspiffs_find_cfg(spiffs_config *cfg, bool force_create) {
-  int i;
-
   if (!force_create) {
-#ifdef SPIFFS_FIXED_LOCATION
-    if (myspiffs_set_cfg(cfg, 0, 0, FALSE)) {
-      return TRUE;
-    }
-#else
-    if (INTERNAL_FLASH_SIZE >= 700000) {
-      for (i = 0; i < 8; i++) {
-	if (myspiffs_set_cfg(cfg, 0x10000, 0x10000 * i, FALSE)) {
-	  return TRUE;
-	}
-      }
-    }
+    int size = SPIFFS_probe_fs(cfg);
 
-    for (i = 0; i < 8; i++) {
-      if (myspiffs_set_cfg(cfg, LOG_BLOCK_SIZE, LOG_BLOCK_SIZE * i, FALSE)) {
-	return TRUE;
-      }
+    if (size > 0 && size < cfg->phys_size) {
+      NODE_DBG("Overriding size:%x\n",size);
+      cfg->phys_size = size;
     }
-#endif
-  }
-
-  // No existing file system -- set up for a format
-  if (INTERNAL_FLASH_SIZE >= 700000) {
-    myspiffs_set_cfg(cfg, 0x10000, 0x10000, TRUE);
-#ifndef SPIFFS_MAX_FILESYSTEM_SIZE
-    if (cfg->phys_size < 400000) {
-      // Don't waste so much in alignment
-      myspiffs_set_cfg(cfg, LOG_BLOCK_SIZE, LOG_BLOCK_SIZE * 4, TRUE);
+    if (size <= 0) {
+      return FALSE;
     }
-#endif
-  } else {
-    myspiffs_set_cfg(cfg, LOG_BLOCK_SIZE, 0, TRUE);
-  }
-
-#ifdef SPIFFS_MAX_FILESYSTEM_SIZE
-  if (cfg->phys_size > SPIFFS_MAX_FILESYSTEM_SIZE) {
-    cfg->phys_size = (SPIFFS_MAX_FILESYSTEM_SIZE) & ~(cfg->log_block_size - 1);
   }
 #endif
 
-  return FALSE;
+  NODE_DBG("myspiffs set cfg block: %x  %x  %x  %x  %x  %x\n", pt_start, pt_end,
+           cfg->phys_size, cfg->phys_addr, cfg->phys_size, cfg->log_block_size);
+
+  return TRUE;
 }
 
-static bool myspiffs_mount_internal(bool force_mount) {
+
+static bool myspiffs_mount(bool force_mount) {
   spiffs_config cfg;
-  if (!myspiffs_find_cfg(&cfg, force_mount) && !force_mount) {
+  if (!myspiffs_set_cfg(&cfg, force_mount) && !force_mount) {
     return FALSE;
   }
 
@@ -189,10 +139,6 @@ static bool myspiffs_mount_internal(bool force_mount) {
   return res == SPIFFS_OK;
 }
 
-bool myspiffs_mount() {
-  return myspiffs_mount_internal(FALSE);
-}
-
 void myspiffs_unmount() {
   SPIFFS_unmount(&fs);
 }
@@ -202,7 +148,7 @@ void myspiffs_unmount() {
 int myspiffs_format( void )
 {
   SPIFFS_unmount(&fs);
-  myspiffs_mount_internal(TRUE);
+  myspiffs_mount(TRUE);
   SPIFFS_unmount(&fs);
 
   NODE_DBG("Formatting: size 0x%x, addr 0x%x\n", fs.cfg.phys_size, fs.cfg.phys_addr);
@@ -211,7 +157,7 @@ int myspiffs_format( void )
     return 0;
   }
 
-  return myspiffs_mount();
+  return myspiffs_mount(FALSE);
 }
 
 #if 0
@@ -555,7 +501,7 @@ static sint32_t myspiffs_vfs_fscfg( uint32_t *phys_addr, uint32_t *phys_size ) {
 
 static vfs_vol  *myspiffs_vfs_mount( const char *name, int num ) {
   // volume descriptor not supported, just return TRUE / FALSE
-  return myspiffs_mount() ? (vfs_vol *)1 : NULL;
+  return myspiffs_mount(FALSE) ? (vfs_vol *)1 : NULL;
 }
 
 static sint32_t myspiffs_vfs_format( void ) {
@@ -574,12 +520,12 @@ static void myspiffs_vfs_clearerr( void ) {
 // ---------------------------------------------------------------------------
 // VFS interface functions
 //
+
 vfs_fs_fns *myspiffs_realm( const char *inname, char **outname, int set_current_drive ) {
   if (inname[0] == '/') {
-    size_t idstr_len = c_strlen( MY_LDRV_ID );
     // logical drive is specified, check if it's our id
-    if (0 == c_strncmp( &(inname[1]), MY_LDRV_ID, idstr_len )) {
-      *outname = (char *)&(inname[1 + idstr_len]);
+    if (0 == c_strncmp(inname + 1, MY_LDRV_ID, sizeof(MY_LDRV_ID)-1)) {
+      *outname = (char *)(inname + sizeof(MY_LDRV_ID));
       if (*outname[0] == '/') {
         // skip leading /
         (*outname)++;

+ 1 - 1
app/spiffs/spiffs_config.h

@@ -165,7 +165,7 @@
 // Lower value generates more read/writes. No meaning having it bigger
 // than logical page size.
 #ifndef SPIFFS_COPY_BUFFER_STACK
-#define SPIFFS_COPY_BUFFER_STACK        (64)
+#define SPIFFS_COPY_BUFFER_STACK        (256)
 #endif
 
 // Enable this to have an identifiable spiffs filesystem. This will look for

+ 0 - 115
app/user/user_exceptions.c

@@ -1,115 +0,0 @@
-/*
- * Copyright 2015 Dius Computing Pty Ltd. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the
- *   distribution.
- * - Neither the name of the copyright holders nor the names of
- *   its contributors may be used to endorse or promote products derived
- *   from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * @author Johny Mattsson <jmattsson@dius.com.au>
- */
-
-#include "user_exceptions.h"
-
-static exception_handler_fn load_store_handler;
-
-void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause)
-{
-  uint32_t val, insn;
-  (void)cause;  /* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */
-
-  asm (
-    /*
-     * Move the aligned content of the exception addr to val
-     */
-    "rsr     a6, EXCVADDR;"    /* read out the faulting address */
-    "movi    a5, ~3;"          /* prepare a mask for the EPC */
-    "and     a5, a5, a6;"      /* apply mask for 32bit aligned base */
-    "l32i    a5, a5, 0;"       /* load aligned value */
-    "ssa8l   a6;"              /* set up shift register for value */
-    "srl     %[val], a5;"      /* shift left to align value */
-                               /* we are done with a6 = EXCVADDR */
-   /*
-    *  Move the aligned instruction to insn
-    */
-    "movi    a5, ~3;"          /* prepare a mask for the insn */
-    "and     a6, a5, %[epc];"  /* apply mask for 32bit aligned base */
-    "l32i    a5, a6, 0;"       /* load part 1 */
-    "l32i    a6, a6, 4;"       /* load part 2 */
-    "ssa8l   %[epc];"          /* set up shift register for src op */
-    "src     %[op], a6, a5;"   /* right shift to get faulting instruction */
-    :[val]"=r"(val), [op]"=r"(insn)
-    :[epc]"r"(ef->epc)
-    :"a5", "a6"
-  );
-
-/* These instructions have the format 0xADSBII where AB = opcode and D = dest reg */
-  uint32_t regno = (insn>>4)&0x0f;                           /* pick out nibble D*/
-  uint32_t opcode = (uint8_t) (((insn>>12)<<4)|(insn&0xf));  /* and nibbles AB */
-#define L8UI  0x02u
-#define L16UI 0x12u
-#define L16SI 0x92u
-
-  if (opcode == L8UI) {                       /* L8UI */
-    val = (uint8_t) val;
-  } else {
-    val = (uint16_t) val;                     /* assume L16SI or L16UI */
-    if (opcode == L16SI) {
-      val = (unsigned)((int)((sint16_t)val)); /* force signed 16->32 bit */
-    } else if (opcode != L16UI) {
-   /*
-    * Anything other than L8UI, L16SI or L16UI then chain to the next handler
-    * if set (typically a remote GDB break). Otherwise execute the default action
-    * which is to trigger a system break and hang if the break doesn't get handled
-    */
-      if (load_store_handler) {
-        load_store_handler(NULL, 0 /* ef ,  cause */);
-        return;
-      } else {
-        asm ("break 1, 1");
-        while (1) {}
-      }
-    }
-  }
-  ef->a_reg[regno ? regno-1: regno] = val; /* carry out the load */
-  ef->epc += 3;                            /* resume at following instruction */
-}
-
-/**
- * The SDK's user_main function installs a debugging handler regardless
- * of whether there's a proper handler installed for EXCCAUSE_LOAD_STORE_ERROR,
- * which of course breaks everything if we allow that to go through. As such,
- * we use the linker to wrap that call and stop the SDK from shooting itself in
- * its proverbial foot. We do save the EXCCAUSE_LOAD_STORE_ERROR handler so that
- * we can chain to it above.
- */
-exception_handler_fn TEXT_SECTION_ATTR
-__wrap__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn)
-{
-  if (cause != EXCCAUSE_LOAD_STORE_ERROR)
-    __real__xtos_set_exception_handler (cause, fn);
-  else
-    load_store_handler = fn;
-}

+ 163 - 126
app/user/user_main.c

@@ -16,7 +16,6 @@
 #include "vfs.h"
 #include "flash_api.h"
 #include "user_interface.h"
-#include "user_exceptions.h"
 #include "user_modules.h"
 
 #include "ets_sys.h"
@@ -24,6 +23,7 @@
 #include "task/task.h"
 #include "mem.h"
 #include "espconn.h"
+#include "sections.h"
 
 #ifdef LUA_USE_MODULES_RTCTIME
 #include "rtc/rtctime.h"
@@ -36,74 +36,163 @@ static uint8 input_sig_flag = 0;
 extern const uint32_t init_data[];
 extern const uint32_t init_data_end[];
 __asm__(
-  /* Place in .text for same reason as user_start_trampoline */
-  ".section \".rodata.dram\"\n"
   ".align 4\n"
-  "init_data:\n"
-  ".incbin \"" ESP_INIT_DATA_DEFAULT "\"\n"
+  "init_data: .incbin \"" ESP_INIT_DATA_DEFAULT "\"\n"
   "init_data_end:\n"
-  ".previous\n"
 );
-
-/* Note: the trampoline *must* be explicitly put into the .text segment, since
- * by the time it is invoked the irom has not yet been mapped. This naturally
- * also goes for anything the trampoline itself calls.
+extern const char _irom0_text_start[], _irom0_text_end[],_flash_used_end[];
+#define IROM0_SIZE (_irom0_text_end - _irom0_text_start)
+
+#define INIT_DATA_SIZE (init_data_end - init_data)
+
+#define PRE_INIT_TEXT_ATTR        __attribute__((section(".p3.pre_init")))
+#define IROM_PTABLE_ATTR          __attribute__((section(".irom0.ptable")))
+
+#define PARTITION(n)  (SYSTEM_PARTITION_CUSTOMER_BEGIN + n)
+
+#define SIZE_256K       0x00040000
+#define SIZE_1024K      0x00100000
+#define FLASH_BASE_ADDR ((char *) 0x40200000)
+
+//TODO: map the TLS server and client certs into NODEMCU_TLSCERT_PARTITION
+const partition_item_t partition_init_table[] IROM_PTABLE_ATTR = {
+    { PARTITION(NODEMCU_EAGLEROM_PARTITION),     0x00000,     0x0B000},
+    { SYSTEM_PARTITION_RF_CAL,                   0x0B000,      0x1000},
+    { SYSTEM_PARTITION_PHY_DATA,                 0x0C000,      0x1000},
+    { SYSTEM_PARTITION_SYSTEM_PARAMETER,         0x0D000,      0x3000},
+    { PARTITION(NODEMCU_IROM0TEXT_PARTITION),    0x10000,      0x0000},
+    { PARTITION(NODEMCU_LFS0_PARTITION),             0x0, LUA_FLASH_STORE},
+    { PARTITION(NODEMCU_SPIFFS0_PARTITION),          0x0, SPIFFS_MAX_FILESYSTEM_SIZE},
+    {0,(uint32_t) &_irom0_text_end,0}
+};
+// The following enum must maintain the partition table order
+enum partition {iram0=0, rf_call, phy_data, sys_parm, irom0, lfs, spiffs};
+#define PTABLE_SIZE ((sizeof(partition_init_table)/sizeof(partition_item_t))-1)
+#define PT_CHUNK 0x8000
+#define PT_ALIGN(n) ((n + (PT_CHUNK-1)) & (~((PT_CHUNK-1))))
+/*
+ * The non-OS SDK prolog has been fundamentally revised in V3.  See SDK EN document
+ * Partition Table.md for further discussion. This version of user_main.c is a
+ * complete rework aligned to V3, with the redundant pre-V3 features removed.
+ *
+ * SDK V3 significantly reduces the RAM footprint required by the SDK and introduces
+ * the use of a partition table (PT) to control flash allocation. The NodeMCU uses
+ * this PT for overall allocation of its flash resources.  A constant copy PT is
+ * maintained at the start of IROM0 (flash offset 0x10000) -- see partition_init_table
+ * declaration above -- to facilitate its modification either in the firmware binary
+ * or in the flash itself. This is Flash PT used during startup to create the live PT
+ * in RAM that is used by the SDK.
+ *
+ * Note that user_pre_init() runs with Icache enabled -- that is the IROM0 partition
+ * is already mapped the address space at 0x40210000 and so that most SDK services
+ * are available, such as system_get_flash_size_map() which returns the valid flash
+ * size (including the 8Mb and 16Mb variants).
+ *
+ * We will be separately releasing a host PC-base python tool to configure the PT,
+ * etc.,  but the following code will initialise the PT to sensible defaults even if
+ * this tool isn't used.
  */
-void TEXT_SECTION_ATTR user_start_trampoline (void)
-{
-   __real__xtos_set_exception_handler (
-     EXCCAUSE_LOAD_STORE_ERROR, load_non_32_wide_handler);
+static int setup_partition_table(partition_item_t *pt, uint32_t *n) {
+
+// Flash size lookup is SIZE_256K*2^N where N is as follows (see SDK/user_interface.h)
+    static char flash_size_scaler[] =
+  /*   0   1   2   3   4   5   6   7   8   9  */
+  /*  ½M  ¼M  1M  2M  4M  2M  4M  4M  8M 16M  */
+   "\001\000\002\003\004\003\004\004\005\006";
+    enum flash_size_map fs_size_code = system_get_flash_size_map();
+    uint32_t flash_size = SIZE_256K << flash_size_scaler[fs_size_code];
+    uint32_t first_free_flash_addr = partition_init_table[PTABLE_SIZE].addr
+                                     - (uint32_t) FLASH_BASE_ADDR;
+    int i,j;
+
+    os_memcpy(pt, partition_init_table, PTABLE_SIZE * sizeof(*pt));
+
+
+    if (flash_size < SIZE_1024K) {
+        os_printf("Flash size (%u) too small to support NodeMCU\n", flash_size);
+        return -1;
+    } else {
+        os_printf("system SPI FI size:%u, Flash size: %u\n", fs_size_code, flash_size );
+    }
+
+// Calculate the runtime sized partitions
+// The iram0, rf_call, phy_data, sys_parm partitions are as-is.
+    if (pt[irom0].size == 0) {
+        pt[irom0].size = first_free_flash_addr - pt[irom0].addr;
+    }
+    if (pt[lfs].addr == 0) {
+        pt[lfs].addr = PT_ALIGN(pt[irom0].addr + pt[irom0].size);
+        os_printf("LFS base: %08X\n", pt[lfs].addr);
+    }
+    if (pt[lfs].size == 0) {
+        pt[lfs].size = 0x10000;
+        os_printf("LFS size: %08X\n", pt[lfs].size);
+    }
+    if (pt[spiffs].addr == 0) {
+        pt[spiffs].addr = PT_ALIGN(pt[lfs].addr + pt[lfs].size);
+        os_printf("SPIFFS base: %08X\n", pt[spiffs].addr);
+    }
+
+    if (pt[spiffs].size == SPIFFS_MAX_FILESYSTEM_SIZE) {
+      pt[spiffs].size = flash_size - pt[spiffs].addr;
+        os_printf("SPIFFS size: %08X\n", pt[spiffs].size);
+    }
+
+//  Check that the phys data partition has been initialised and if not then do this
+//  now to prevent the SDK halting on a "rf_cal[0] !=0x05,is 0xFF" error.
+    uint32_t init_data_hdr = 0xffffffff, data_addr = pt[phy_data].addr;
+    int status = spi_flash_read(data_addr, &init_data_hdr, sizeof (uint32_t));
+    if (status == SPI_FLASH_RESULT_OK && *(char *)&init_data_hdr != 0x05) {
+        uint32_t idata[INIT_DATA_SIZE];
+        os_printf("Writing Init Data to 0x%08x\n",data_addr);
+        spi_flash_erase_sector(data_addr/SPI_FLASH_SEC_SIZE);
+        os_memcpy(idata, init_data, sizeof(idata));
+        spi_flash_write(data_addr, idata, sizeof(idata));
+        os_delay_us(1000);
+    }
+
+// Check partitions are page aligned and remove and any zero-length partitions.
+// This must be done last as this might break the enum partition ordering.
+    for (i = 0, j = 0; i < PTABLE_SIZE; i++) {
+        const partition_item_t *p = pt + i;
+        if ((p->addr| p->size) & (SPI_FLASH_SEC_SIZE-1)) {
+            os_printf("Partitions must be flash page aligned\n");
+            return -1;
+        }
+        if (p->size == 0)
+          continue;
+        if (j < i) {
+          pt[j] = *p;
+          p = pt + j;
+        }
+        os_printf("%2u: %08x %08x %08x\n", j, p->type, p->addr, p->size);
+        j++;
+    }
+
+    *n = j;
+    return fs_size_code;
+}
 
+void user_pre_init(void) {
 #ifdef LUA_USE_MODULES_RTCTIME
   // Note: Keep this as close to call_user_start() as possible, since it
   // is where the cpu clock actually gets bumped to 80MHz.
-  rtctime_early_startup ();
+    rtctime_early_startup ();
 #endif
+    static partition_item_t pt[PTABLE_SIZE];
 
-  /* Re-implementation of default init data deployment. The SDK does not
-   * appear to be laying down its own version of init data anymore, so
-   * we have to do it again. To see whether we need to, we read out
-   * the flash size and do a test for esp_init_data based on that size.
-   * If it's missing, we need to initialize it *right now* before the SDK
-   * starts up and gets stuck at "rf_cal[0] !=0x05,is 0xFF".
-   * If the size byte is wrong, then we'll end up fixing up the init data
-   * again on the next boot, after we've corrected the size byte.
-   * Only remaining issue is lack of spare code bytes in iram, so this
-   * is deliberately quite terse and not as readable as one might like.
-   */
-  SPIFlashInfo sfi;
-
-  // enable operations on >4MB flash chip
-  extern SpiFlashChip * flashchip;
-  uint32 orig_chip_size = flashchip->chip_size;
-  flashchip->chip_size = FLASH_SIZE_16MBYTE;
-
-  SPIRead (0, (uint32_t *)(&sfi), sizeof (sfi)); // Cache read not enabled yet, safe to use
-  // handle all size entries
-  switch (sfi.size) {
-  case 0: sfi.size = 1; break; // SIZE_4MBIT
-  case 1: sfi.size = 0; break; // SIZE_2MBIT
-  case 5: sfi.size = 3; break; // SIZE_16MBIT_8M_8M
-  case 6: // fall-through
-  case 7: sfi.size = 4; break; // SIZE_32MBIT_8M_8M, SIZE_32MBIT_16M_16M
-  case 8: sfi.size = 5; break; // SIZE_64MBIT
-  case 9: sfi.size = 6; break; // SIZE_128MBIT
-  default: break;
-  }
-  uint32_t flash_end_addr = (256 * 1024) << sfi.size;
-  uint32_t init_data_hdr = 0xffffffff;
-  uint32_t init_data_addr = flash_end_addr - 4 * SPI_FLASH_SEC_SIZE;
-  SPIRead (init_data_addr, &init_data_hdr, sizeof (init_data_hdr));
-  if (init_data_hdr == 0xffffffff)
-  {
-    SPIEraseSector (init_data_addr);
-    SPIWrite (init_data_addr, init_data, 4 * (init_data_end - init_data));
-  }
+    uint32_t pt_size;
+    uint32_t fs_size_code = setup_partition_table(pt, &pt_size);
+    if( fs_size_code > 0 && system_partition_table_regist(pt, pt_size, fs_size_code)) {
+        return;
+    }
+    os_printf("system_partition_table_regist fail (%u)\n", fs_size_code);
+    while(1);
 
-  // revert temporary setting
-  flashchip->chip_size = orig_chip_size;
+}
 
-  call_user_start ();
+uint32 ICACHE_RAM_ATTR user_iram_memory_is_enabled(void) {
+    return FALSE;  // NodeMCU runs like a dog if iRAM is enabled
 }
 
 // +================== New task interface ==================+
@@ -138,32 +227,17 @@ void nodemcu_init(void) {
         NODE_DBG("Can not init platform for modules.\n");
         return;
     }
-    uint32_t size_detected = flash_detect_size_byte();
-    uint32_t size_from_rom = flash_rom_get_size_byte();
-    if( size_detected != size_from_rom ) {
-        NODE_ERR("Self adjust flash size. 0x%x (ROM) -> 0x%x (Detected)\n",
-                 size_from_rom, size_detected);
-        // Fit hardware real flash size.
-        flash_rom_set_size_byte(size_detected);
-
-        system_restart ();
-        // Don't post the start_lua task, we're about to reboot...
-        return;
-    }
 
 #ifdef BUILD_SPIFFS
     if (!vfs_mount("/FLASH", 0)) {
         // Failed to mount -- try reformat
-	dbg_printf("Formatting file system. Please wait...\n");
+        dbg_printf("Formatting file system. Please wait...\n");
         if (!vfs_format()) {
             NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
             NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
         }
-        // Note that fs_format leaves the file system mounted
     }
-    // test_spiffs();
 #endif
-    // endpoint_setup();
 
     if (!task_post_low(task_get_id(start_lua),'s'))
       NODE_ERR("Failed to post the start_lua task!\n");
@@ -179,59 +253,6 @@ void user_rf_pre_init(void)
 }
 #endif
 
-/******************************************************************************
- * FunctionName : user_rf_cal_sector_set
- * Description  : SDK just reversed 4 sectors, used for rf init data and paramters.
- *                We add this function to force users to set rf cal sector, since
- *                we don't know which sector is free in user's application.
- *                sector map for last several sectors : ABCCC
- *                A : rf cal
- *                B : rf init data
- *                C : sdk parameters
- * Parameters   : none
- * Returns      : rf cal sector
-*******************************************************************************/
-uint32
-user_rf_cal_sector_set(void)
-{
-    enum flash_size_map size_map = system_get_flash_size_map();
-    uint32 rf_cal_sec = 0;
-
-    switch (size_map) {
-        case FLASH_SIZE_4M_MAP_256_256:
-            rf_cal_sec = 128 - 5;
-            break;
-
-        case FLASH_SIZE_8M_MAP_512_512:
-            rf_cal_sec = 256 - 5;
-            break;
-
-        case FLASH_SIZE_16M_MAP_512_512:
-        case FLASH_SIZE_16M_MAP_1024_1024:
-            rf_cal_sec = 512 - 5;
-            break;
-
-        case FLASH_SIZE_32M_MAP_512_512:
-        case FLASH_SIZE_32M_MAP_1024_1024:
-        case FLASH_SIZE_32M_MAP_2048_2048:
-            rf_cal_sec = 1024 - 5;
-            break;
-
-        case FLASH_SIZE_64M_MAP_1024_1024:
-            rf_cal_sec = 2048 - 5;
-            break;
-
-        case FLASH_SIZE_128M_MAP_1024_1024:
-            rf_cal_sec = 4096 - 5;
-            break;
-
-        default:
-            rf_cal_sec = 0;
-            break;
-    }
-
-    return rf_cal_sec;
-}
 
 /******************************************************************************
  * FunctionName : user_init
@@ -241,6 +262,7 @@ user_rf_cal_sector_set(void)
 *******************************************************************************/
 void user_init(void)
 {
+
 #ifdef LUA_USE_MODULES_RTCTIME
     rtctime_late_startup ();
 #endif
@@ -255,3 +277,18 @@ void user_init(void)
 #endif
     system_init_done_cb(nodemcu_init);
 }
+
+/*
+ * The SDK now establishes exception handlers for EXCCAUSE errors: ILLEGAL,
+ * INSTR_ERROR, LOAD_STORE_ERROR, PRIVILEGED, UNALIGNED, LOAD_PROHIBITED,
+ * STORE_PROHIBITED.  These handlers are established in SDK/app_main.c.
+ * LOAD_STORE_ERROR is handled by SDK/user_exceptions.o:load_non_32_wide_handler()
+ * which is a fork of our version. The remaining are handled by a static function
+ * at SDK:app+main.c:offset 0x0348.
+ *
+void __real__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn);
+void __wrap__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn) {
+    os_printf("Exception handler %x  %x\n", cause, fn);
+    __real__xtos_set_exception_handler (cause, fn);
+}
+ */

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
docs/compiling.md


Файловите разлики са ограничени, защото са твърде много
+ 5 - 12
docs/getting-started.md


+ 1 - 1
docs/modules/node.md

@@ -183,7 +183,7 @@ Returns the function reference for a function in the [LFS (Lua Flash Store)](../
 `modulename`  The name of the module to be loaded.  If this is `nil` or invalid then an info list is returned
 
 #### Returns
--  In the case where the LFS in not loaded, `node.flashindex` evaluates to `nil`, followed by the flash and mapped base addresss of the LFS
+-  In the case where the LFS in not loaded, `node.flashindex` evaluates to `nil`, followed by the flash mapped base addresss of the LFS, its flash offset, and the size of the LFS.
 -  If the LFS is loaded and the function is called with the name of a valid module in the LFS, then the function is returned in the same way the `load()` and the other Lua load functions do.
 -  Otherwise an extended info list is returned: the Unix time of the LFS build, the flash and mapped base addresses of the LFS and its current length, and an array of the valid module names in the LFS.
 

+ 13 - 1
ld/nodemcu.ld

@@ -5,6 +5,7 @@ MEMORY
   dport0_0_seg :                        org = 0x3FF00000, len = 0x10
   dram0_0_seg :                         org = 0x3FFE8000, len = 0x14000
   iram1_0_seg :                         org = 0x40100000, len = 0x8000
+  iram0_0_seg :                         org = 0x4010E000, len = 0x2000
   irom0_0_seg :                         org = 0x40210000, len = 0xE0000
 }
 
@@ -14,12 +15,13 @@ PHDRS
   dram0_0_phdr PT_LOAD;
   dram0_0_bss_phdr PT_LOAD;
   iram1_0_phdr PT_LOAD;
+  iram0_0_phdr PT_LOAD;
   irom0_0_phdr PT_LOAD;
 }
 
 
 /*  Default entry point:  */
-ENTRY(user_start_trampoline)
+ENTRY(call_user_start)
 EXTERN(_DebugExceptionVector)
 EXTERN(_DoubleExceptionVector)
 EXTERN(_KernelExceptionVector)
@@ -221,11 +223,21 @@ SECTIONS
     _lit4_end = ABSOLUTE(.);
   } >iram1_0_seg :iram1_0_phdr
 
+  .pre_init_ram : ALIGN(0x1000)
+  {
+    _iram0_start = ABSOLUTE(.);
+    *(*.pre_init)
+    _iram0_end = ABSOLUTE(.);
+  } >iram0_0_seg :iram0_0_phdr
+
   .irom0.text : ALIGN(0x1000)
   {
     _irom0_text_start = ABSOLUTE(.);
+    *(.irom0.ptable)
+    . = ALIGN(0x1000);
     *(.servercert.flash)
     *(.clientcert.flash)
+    . = ALIGN(0x1000);
     *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text)
     *(.literal .text .literal.* .text.*)
     *(.rodata*)

+ 0 - 11
sdk-overrides/include/mem.h

@@ -1,11 +0,0 @@
-#ifndef _SDK_OVERRIDE_MEM_H_
-#define _SDK_OVERRIDE_MEM_H_
-
-void *pvPortMalloc (size_t sz, const char *, unsigned);
-void vPortFree (void *p, const char *, unsigned);
-void *pvPortZalloc (size_t sz, const char *, unsigned);
-void *pvPortRealloc (void *p, size_t n, const char *, unsigned);
-
-#include_next "mem.h"
-
-#endif

+ 0 - 1236
tools/esptool.py

@@ -1,1236 +0,0 @@
-#!/usr/bin/env python
-# NB: Before sending a PR to change the above line to '#!/usr/bin/env python2', please read https://github.com/themadinventor/esptool/issues/21
-#
-# ESP8266 ROM Bootloader Utility
-# https://github.com/themadinventor/esptool
-#
-# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, other contributors as noted.
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
-# Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import argparse
-import hashlib
-import inspect
-import json
-import os
-import serial
-import struct
-import subprocess
-import sys
-import tempfile
-import time
-
-
-__version__ = "1.2-dev"
-
-
-class ESPROM(object):
-    # These are the currently known commands supported by the ROM
-    ESP_FLASH_BEGIN = 0x02
-    ESP_FLASH_DATA  = 0x03
-    ESP_FLASH_END   = 0x04
-    ESP_MEM_BEGIN   = 0x05
-    ESP_MEM_END     = 0x06
-    ESP_MEM_DATA    = 0x07
-    ESP_SYNC        = 0x08
-    ESP_WRITE_REG   = 0x09
-    ESP_READ_REG    = 0x0a
-
-    # Maximum block sized for RAM and Flash writes, respectively.
-    ESP_RAM_BLOCK   = 0x1800
-    ESP_FLASH_BLOCK = 0x400
-
-    # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want.
-    ESP_ROM_BAUD    = 115200
-
-    # First byte of the application image
-    ESP_IMAGE_MAGIC = 0xe9
-
-    # Initial state for the checksum routine
-    ESP_CHECKSUM_MAGIC = 0xef
-
-    # OTP ROM addresses
-    ESP_OTP_MAC0    = 0x3ff00050
-    ESP_OTP_MAC1    = 0x3ff00054
-    ESP_OTP_MAC3    = 0x3ff0005c
-
-    # Flash sector size, minimum unit of erase.
-    ESP_FLASH_SECTOR = 0x1000
-
-    def __init__(self, port=0, baud=ESP_ROM_BAUD):
-        self._port = serial.Serial(port)
-        self._slip_reader = slip_reader(port)
-        # setting baud rate in a separate step is a workaround for
-        # CH341 driver on some Linux versions (this opens at 9600 then
-        # sets), shouldn't matter for other platforms/drivers. See
-        # https://github.com/themadinventor/esptool/issues/44#issuecomment-107094446
-        self._port.baudrate = baud
-
-    """ Read a SLIP packet from the serial port """
-    def read(self):
-        return self._slip_reader.next()
-
-    """ Write bytes to the serial port while performing SLIP escaping """
-    def write(self, packet):
-        buf = '\xc0' \
-              + (packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc')) \
-              + '\xc0'
-        self._port.write(buf)
-
-    """ Calculate checksum of a blob, as it is defined by the ROM """
-    @staticmethod
-    def checksum(data, state=ESP_CHECKSUM_MAGIC):
-        for b in data:
-            state ^= ord(b)
-        return state
-
-    """ Send a request and read the response """
-    def command(self, op=None, data=None, chk=0):
-        if op is not None:
-            pkt = struct.pack('<BBHI', 0x00, op, len(data), chk) + data
-            self.write(pkt)
-
-        # tries to get a response until that response has the
-        # same operation as the request or a retries limit has
-        # exceeded. This is needed for some esp8266s that
-        # reply with more sync responses than expected.
-        for retry in xrange(100):
-            p = self.read()
-            if len(p) < 8:
-                continue
-            (resp, op_ret, len_ret, val) = struct.unpack('<BBHI', p[:8])
-            if resp != 1:
-                continue
-            body = p[8:]
-            if op is None or op_ret == op:
-                return val, body  # valid response received
-
-        raise FatalError("Response doesn't match request")
-
-    """ Perform a connection test """
-    def sync(self):
-        self.command(ESPROM.ESP_SYNC, '\x07\x07\x12\x20' + 32 * '\x55')
-        for i in xrange(7):
-            self.command()
-
-    """ Try connecting repeatedly until successful, or giving up """
-    def connect(self):
-        print 'Connecting...'
-
-        for _ in xrange(4):
-            # issue reset-to-bootloader:
-            # RTS = either CH_PD or nRESET (both active low = chip in reset)
-            # DTR = GPIO0 (active low = boot to flasher)
-            self._port.setDTR(False)
-            self._port.setRTS(True)
-            time.sleep(0.05)
-            self._port.setDTR(True)
-            self._port.setRTS(False)
-            time.sleep(0.05)
-            self._port.setDTR(False)
-
-            # worst-case latency timer should be 255ms (probably <20ms)
-            self._port.timeout = 0.3
-            for _ in xrange(4):
-                try:
-                    self._port.flushInput()
-                    self._slip_reader = slip_reader(self._port)
-                    self._port.flushOutput()
-                    self.sync()
-                    self._port.timeout = 5
-                    return
-                except:
-                    time.sleep(0.05)
-        raise FatalError('Failed to connect to ESP8266')
-
-    """ Read memory address in target """
-    def read_reg(self, addr):
-        res = self.command(ESPROM.ESP_READ_REG, struct.pack('<I', addr))
-        if res[1] != "\0\0":
-            raise FatalError('Failed to read target memory')
-        return res[0]
-
-    """ Write to memory address in target """
-    def write_reg(self, addr, value, mask, delay_us=0):
-        if self.command(ESPROM.ESP_WRITE_REG,
-                        struct.pack('<IIII', addr, value, mask, delay_us))[1] != "\0\0":
-            raise FatalError('Failed to write target memory')
-
-    """ Start downloading an application image to RAM """
-    def mem_begin(self, size, blocks, blocksize, offset):
-        if self.command(ESPROM.ESP_MEM_BEGIN,
-                        struct.pack('<IIII', size, blocks, blocksize, offset))[1] != "\0\0":
-            raise FatalError('Failed to enter RAM download mode')
-
-    """ Send a block of an image to RAM """
-    def mem_block(self, data, seq):
-        if self.command(ESPROM.ESP_MEM_DATA,
-                        struct.pack('<IIII', len(data), seq, 0, 0) + data,
-                        ESPROM.checksum(data))[1] != "\0\0":
-            raise FatalError('Failed to write to target RAM')
-
-    """ Leave download mode and run the application """
-    def mem_finish(self, entrypoint=0):
-        if self.command(ESPROM.ESP_MEM_END,
-                        struct.pack('<II', int(entrypoint == 0), entrypoint))[1] != "\0\0":
-            raise FatalError('Failed to leave RAM download mode')
-
-    """ Start downloading to Flash (performs an erase) """
-    def flash_begin(self, size, offset):
-        old_tmo = self._port.timeout
-        num_blocks = (size + ESPROM.ESP_FLASH_BLOCK - 1) / ESPROM.ESP_FLASH_BLOCK
-
-        sectors_per_block = 16
-        sector_size = self.ESP_FLASH_SECTOR
-        num_sectors = (size + sector_size - 1) / sector_size
-        start_sector = offset / sector_size
-
-        head_sectors = sectors_per_block - (start_sector % sectors_per_block)
-        if num_sectors < head_sectors:
-            head_sectors = num_sectors
-
-        if num_sectors < 2 * head_sectors:
-            erase_size = (num_sectors + 1) / 2 * sector_size
-        else:
-            erase_size = (num_sectors - head_sectors) * sector_size
-
-        self._port.timeout = 20
-        t = time.time()
-        result = self.command(ESPROM.ESP_FLASH_BEGIN,
-                              struct.pack('<IIII', erase_size, num_blocks, ESPROM.ESP_FLASH_BLOCK, offset))[1]
-        if size != 0:
-            print "Took %.2fs to erase flash block" % (time.time() - t)
-        if result != "\0\0":
-            raise FatalError.WithResult('Failed to enter Flash download mode (result "%s")', result)
-        self._port.timeout = old_tmo
-
-    """ Write block to flash """
-    def flash_block(self, data, seq):
-        result = self.command(ESPROM.ESP_FLASH_DATA,
-                              struct.pack('<IIII', len(data), seq, 0, 0) + data,
-                              ESPROM.checksum(data))[1]
-        if result != "\0\0":
-            raise FatalError.WithResult('Failed to write to target Flash after seq %d (got result %%s)' % seq, result)
-
-    """ Leave flash mode and run/reboot """
-    def flash_finish(self, reboot=False):
-        pkt = struct.pack('<I', int(not reboot))
-        if self.command(ESPROM.ESP_FLASH_END, pkt)[1] != "\0\0":
-            raise FatalError('Failed to leave Flash mode')
-
-    """ Run application code in flash """
-    def run(self, reboot=False):
-        # Fake flash begin immediately followed by flash end
-        self.flash_begin(0, 0)
-        self.flash_finish(reboot)
-
-    """ Read MAC from OTP ROM """
-    def read_mac(self):
-        mac0 = self.read_reg(self.ESP_OTP_MAC0)
-        mac1 = self.read_reg(self.ESP_OTP_MAC1)
-        mac3 = self.read_reg(self.ESP_OTP_MAC3)
-        if (mac3 != 0):
-            oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff)
-        elif ((mac1 >> 16) & 0xff) == 0:
-            oui = (0x18, 0xfe, 0x34)
-        elif ((mac1 >> 16) & 0xff) == 1:
-            oui = (0xac, 0xd0, 0x74)
-        else:
-            raise FatalError("Unknown OUI")
-        return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff)
-
-    """ Read Chip ID from OTP ROM - see http://esp8266-re.foogod.com/wiki/System_get_chip_id_%28IoT_RTOS_SDK_0.9.9%29 """
-    def chip_id(self):
-        id0 = self.read_reg(self.ESP_OTP_MAC0)
-        id1 = self.read_reg(self.ESP_OTP_MAC1)
-        return (id0 >> 24) | ((id1 & 0xffffff) << 8)
-
-    """ Read SPI flash manufacturer and device id """
-    def flash_id(self):
-        self.flash_begin(0, 0)
-        self.write_reg(0x60000240, 0x0, 0xffffffff)
-        self.write_reg(0x60000200, 0x10000000, 0xffffffff)
-        flash_id = self.read_reg(0x60000240)
-        self.flash_finish(False)
-        return flash_id
-
-    """ Abuse the loader protocol to force flash to be left in write mode """
-    def flash_unlock_dio(self):
-        # Enable flash write mode
-        self.flash_begin(0, 0)
-        # Reset the chip rather than call flash_finish(), which would have
-        # write protected the chip again (why oh why does it do that?!)
-        self.mem_begin(0,0,0,0x40100000)
-        self.mem_finish(0x40000080)
-
-    """ Perform a chip erase of SPI flash """
-    def flash_erase(self):
-        # Trick ROM to initialize SFlash
-        self.flash_begin(0, 0)
-
-        # This is hacky: we don't have a custom stub, instead we trick
-        # the bootloader to jump to the SPIEraseChip() routine and then halt/crash
-        # when it tries to boot an unconfigured system.
-        self.mem_begin(0,0,0,0x40100000)
-        self.mem_finish(0x40004984)
-
-        # Yup - there's no good way to detect if we succeeded.
-        # It it on the other hand unlikely to fail.
-
-    def run_stub(self, stub, params, read_output=True):
-        stub = dict(stub)
-        stub['code'] = unhexify(stub['code'])
-        if 'data' in stub:
-            stub['data'] = unhexify(stub['data'])
-
-        if stub['num_params'] != len(params):
-            raise FatalError('Stub requires %d params, %d provided'
-                             % (stub['num_params'], len(params)))
-
-        params = struct.pack('<' + ('I' * stub['num_params']), *params)
-        pc = params + stub['code']
-
-        # Upload
-        self.mem_begin(len(pc), 1, len(pc), stub['params_start'])
-        self.mem_block(pc, 0)
-        if 'data' in stub:
-            self.mem_begin(len(stub['data']), 1, len(stub['data']), stub['data_start'])
-            self.mem_block(stub['data'], 0)
-        self.mem_finish(stub['entry'])
-
-        if read_output:
-            print 'Stub executed, reading response:'
-            while True:
-                p = self.read()
-                print hexify(p)
-                if p == '':
-                    return
-
-
-class ESPBOOTLOADER(object):
-    """ These are constants related to software ESP bootloader, working with 'v2' image files """
-
-    # First byte of the "v2" application image
-    IMAGE_V2_MAGIC = 0xea
-
-    # First 'segment' value in a "v2" application image, appears to be a constant version value?
-    IMAGE_V2_SEGMENT = 4
-
-
-def LoadFirmwareImage(filename):
-    """ Load a firmware image, without knowing what kind of file (v1 or v2) it is.
-
-        Returns a BaseFirmwareImage subclass, either ESPFirmwareImage (v1) or OTAFirmwareImage (v2).
-    """
-    with open(filename, 'rb') as f:
-        magic = ord(f.read(1))
-        f.seek(0)
-        if magic == ESPROM.ESP_IMAGE_MAGIC:
-            return ESPFirmwareImage(f)
-        elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC:
-            return OTAFirmwareImage(f)
-        else:
-            raise FatalError("Invalid image magic number: %d" % magic)
-
-
-class BaseFirmwareImage(object):
-    """ Base class with common firmware image functions """
-    def __init__(self):
-        self.segments = []
-        self.entrypoint = 0
-
-    def add_segment(self, addr, data, pad_to=4):
-        """ Add a segment to the image, with specified address & data
-        (padded to a boundary of pad_to size) """
-        # Data should be aligned on word boundary
-        l = len(data)
-        if l % pad_to:
-            data += b"\x00" * (pad_to - l % pad_to)
-        if l > 0:
-            self.segments.append((addr, len(data), data))
-
-    def load_segment(self, f, is_irom_segment=False):
-        """ Load the next segment from the image file """
-        (offset, size) = struct.unpack('<II', f.read(8))
-        if not is_irom_segment:
-            if offset > 0x40200000 or offset < 0x3ffe0000 or size > 65536:
-                raise FatalError('Suspicious segment 0x%x, length %d' % (offset, size))
-        segment_data = f.read(size)
-        if len(segment_data) < size:
-            raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data)))
-        segment = (offset, size, segment_data)
-        self.segments.append(segment)
-        return segment
-
-    def save_segment(self, f, segment, checksum=None):
-        """ Save the next segment to the image file, return next checksum value if provided """
-        (offset, size, data) = segment
-        f.write(struct.pack('<II', offset, size))
-        f.write(data)
-        if checksum is not None:
-            return ESPROM.checksum(data, checksum)
-
-    def read_checksum(self, f):
-        """ Return ESPROM checksum from end of just-read image """
-        # Skip the padding. The checksum is stored in the last byte so that the
-        # file is a multiple of 16 bytes.
-        align_file_position(f, 16)
-        return ord(f.read(1))
-
-    def append_checksum(self, f, checksum):
-        """ Append ESPROM checksum to the just-written image """
-        align_file_position(f, 16)
-        f.write(struct.pack('B', checksum))
-
-    def write_v1_header(self, f, segments):
-        f.write(struct.pack('<BBBBI', ESPROM.ESP_IMAGE_MAGIC, len(segments),
-                            self.flash_mode, self.flash_size_freq, self.entrypoint))
-
-
-class ESPFirmwareImage(BaseFirmwareImage):
-    """ 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """
-    def __init__(self, load_file=None):
-        super(ESPFirmwareImage, self).__init__()
-        self.flash_mode = 0
-        self.flash_size_freq = 0
-        self.version = 1
-
-        if load_file is not None:
-            (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack('<BBBBI', load_file.read(8))
-
-            # some sanity check
-            if magic != ESPROM.ESP_IMAGE_MAGIC or segments > 16:
-                raise FatalError('Invalid firmware image magic=%d segments=%d' % (magic, segments))
-
-            for i in xrange(segments):
-                self.load_segment(load_file)
-            self.checksum = self.read_checksum(load_file)
-
-    def save(self, filename):
-        with open(filename, 'wb') as f:
-            self.write_v1_header(f, self.segments)
-            checksum = ESPROM.ESP_CHECKSUM_MAGIC
-            for segment in self.segments:
-                checksum = self.save_segment(f, segment, checksum)
-            self.append_checksum(f, checksum)
-
-
-class OTAFirmwareImage(BaseFirmwareImage):
-    """ 'Version 2' firmware image, segments loaded by software bootloader stub
-        (ie Espressif bootloader or rboot)
-    """
-    def __init__(self, load_file=None):
-        super(OTAFirmwareImage, self).__init__()
-        self.version = 2
-        if load_file is not None:
-            (magic, segments, first_flash_mode, first_flash_size_freq, first_entrypoint) = struct.unpack('<BBBBI', load_file.read(8))
-
-            # some sanity check
-            if magic != ESPBOOTLOADER.IMAGE_V2_MAGIC:
-                raise FatalError('Invalid V2 image magic=%d' % (magic))
-            if segments != 4:
-                # segment count is not really segment count here, but we expect to see '4'
-                print 'Warning: V2 header has unexpected "segment" count %d (usually 4)' % segments
-
-            # irom segment comes before the second header
-            self.load_segment(load_file, True)
-
-            (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack('<BBBBI', load_file.read(8))
-
-            if first_flash_mode != self.flash_mode:
-                print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.'
-                      % (first_flash_mode, self.flash_mode))
-            if first_flash_size_freq != self.flash_size_freq:
-                print('WARNING: Flash size/freq value in first header (0x%02x) disagrees with second (0x%02x). Using second value.'
-                      % (first_flash_size_freq, self.flash_size_freq))
-            if first_entrypoint != self.entrypoint:
-                print('WARNING: Enterypoint address in first header (0x%08x) disagrees with second header (0x%08x). Using second value.'
-                      % (first_entrypoint, self.entrypoint))
-
-            if magic != ESPROM.ESP_IMAGE_MAGIC or segments > 16:
-                raise FatalError('Invalid V2 second header magic=%d segments=%d' % (magic, segments))
-
-            # load all the usual segments
-            for _ in xrange(segments):
-                self.load_segment(load_file)
-            self.checksum = self.read_checksum(load_file)
-
-    def save(self, filename):
-        with open(filename, 'wb') as f:
-            # Save first header for irom0 segment
-            f.write(struct.pack('<BBBBI', ESPBOOTLOADER.IMAGE_V2_MAGIC, ESPBOOTLOADER.IMAGE_V2_SEGMENT,
-                                self.flash_mode, self.flash_size_freq, self.entrypoint))
-
-            # irom0 segment identified by load address zero
-            irom_segments = [segment for segment in self.segments if segment[0] == 0]
-            if len(irom_segments) != 1:
-                raise FatalError('Found %d segments that could be irom0. Bad ELF file?' % len(irom_segments))
-            # save irom0 segment
-            irom_segment = irom_segments[0]
-            self.save_segment(f, irom_segment)
-
-            # second header, matches V1 header and contains loadable segments
-            normal_segments = [s for s in self.segments if s != irom_segment]
-            self.write_v1_header(f, normal_segments)
-            checksum = ESPROM.ESP_CHECKSUM_MAGIC
-            for segment in normal_segments:
-                checksum = self.save_segment(f, segment, checksum)
-            self.append_checksum(f, checksum)
-
-
-class ELFFile(object):
-    def __init__(self, name):
-        self.name = binutils_safe_path(name)
-        self.symbols = None
-
-    def _fetch_symbols(self):
-        if self.symbols is not None:
-            return
-        self.symbols = {}
-        try:
-            tool_nm = "xtensa-lx106-elf-nm"
-            if os.getenv('XTENSA_CORE') == 'lx106':
-                tool_nm = "xt-nm"
-            proc = subprocess.Popen([tool_nm, self.name], stdout=subprocess.PIPE)
-        except OSError:
-            print "Error calling %s, do you have Xtensa toolchain in PATH?" % tool_nm
-            sys.exit(1)
-        for l in proc.stdout:
-            fields = l.strip().split()
-            try:
-                if fields[0] == "U":
-                    print "Warning: ELF binary has undefined symbol %s" % fields[1]
-                    continue
-                if fields[0] == "w":
-                    continue  # can skip weak symbols
-                self.symbols[fields[2]] = int(fields[0], 16)
-            except ValueError:
-                raise FatalError("Failed to strip symbol output from nm: %s" % fields)
-
-    def get_symbol_addr(self, sym):
-        self._fetch_symbols()
-        return self.symbols[sym]
-
-    def get_entry_point(self):
-        tool_readelf = "xtensa-lx106-elf-readelf"
-        if os.getenv('XTENSA_CORE') == 'lx106':
-            tool_readelf = "xt-readelf"
-        try:
-            proc = subprocess.Popen([tool_readelf, "-h", self.name], stdout=subprocess.PIPE)
-        except OSError:
-            print "Error calling %s, do you have Xtensa toolchain in PATH?" % tool_readelf
-            sys.exit(1)
-        for l in proc.stdout:
-            fields = l.strip().split()
-            if fields[0] == "Entry":
-                return int(fields[3], 0)
-
-    def load_section(self, section):
-        tool_objcopy = "xtensa-lx106-elf-objcopy"
-        if os.getenv('XTENSA_CORE') == 'lx106':
-            tool_objcopy = "xt-objcopy"
-        tmpsection = binutils_safe_path(tempfile.mktemp(suffix=".section"))
-        try:
-            subprocess.check_call([tool_objcopy, "--only-section", section, "-Obinary", self.name, tmpsection])
-            with open(tmpsection, "rb") as f:
-                data = f.read()
-        finally:
-            os.remove(tmpsection)
-        return data
-
-
-class CesantaFlasher(object):
-
-    # From stub_flasher.h
-    CMD_FLASH_WRITE = 1
-    CMD_FLASH_READ = 2
-    CMD_FLASH_DIGEST = 3
-    CMD_BOOT_FW = 6
-
-    def __init__(self, esp, baud_rate=0):
-        print 'Running Cesanta flasher stub...'
-        if baud_rate <= ESPROM.ESP_ROM_BAUD:  # don't change baud rates if we already synced at that rate
-            baud_rate = 0
-        self._esp = esp
-        esp.run_stub(json.loads(_CESANTA_FLASHER_STUB), [baud_rate], read_output=False)
-        if baud_rate > 0:
-            esp._port.baudrate = baud_rate
-        # Read the greeting.
-        p = esp.read()
-        if p != 'OHAI':
-            raise FatalError('Failed to connect to the flasher (got %s)' % hexify(p))
-
-    def flash_write(self, addr, data, show_progress=False):
-        assert addr % self._esp.ESP_FLASH_SECTOR == 0, 'Address must be sector-aligned'
-        assert len(data) % self._esp.ESP_FLASH_SECTOR == 0, 'Length must be sector-aligned'
-        sys.stdout.write('Writing %d @ 0x%x... ' % (len(data), addr))
-        sys.stdout.flush()
-        self._esp.write(struct.pack('<B', self.CMD_FLASH_WRITE))
-        self._esp.write(struct.pack('<III', addr, len(data), 1))
-        num_sent, num_written = 0, 0
-        while num_written < len(data):
-            p = self._esp.read()
-            if len(p) == 4:
-                num_written = struct.unpack('<I', p)[0]
-            elif len(p) == 1:
-                status_code = struct.unpack('<B', p)[0]
-                raise FatalError('Write failure, status: %x' % status_code)
-            else:
-                raise FatalError('Unexpected packet while writing: %s' % hexify(p))
-            if show_progress:
-                progress = '%d (%d %%)' % (num_written, num_written * 100.0 / len(data))
-                sys.stdout.write(progress + '\b' * len(progress))
-                sys.stdout.flush()
-            while num_sent - num_written < 5120:
-                self._esp._port.write(data[num_sent:num_sent + 1024])
-                num_sent += 1024
-        p = self._esp.read()
-        if len(p) != 16:
-            raise FatalError('Expected digest, got: %s' % hexify(p))
-        digest = hexify(p).upper()
-        expected_digest = hashlib.md5(data).hexdigest().upper()
-        print
-        if digest != expected_digest:
-            raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest))
-        p = self._esp.read()
-        if len(p) != 1:
-            raise FatalError('Expected status, got: %s' % hexify(p))
-        status_code = struct.unpack('<B', p)[0]
-        if status_code != 0:
-            raise FatalError('Write failure, status: %x' % status_code)
-
-    def flash_read(self, addr, length, show_progress=False):
-        sys.stdout.write('Reading %d @ 0x%x... ' % (length, addr))
-        sys.stdout.flush()
-        self._esp.write(struct.pack('<B', self.CMD_FLASH_READ))
-        # USB may not be able to keep up with the read rate, especially at
-        # higher speeds. Since we don't have flow control, this will result in
-        # data loss. Hence, we use small packet size and only allow small
-        # number of bytes in flight, which we can reasonably expect to fit in
-        # the on-chip FIFO. max_in_flight = 64 works for CH340G, other chips may
-        # have longer FIFOs and could benefit from increasing max_in_flight.
-        self._esp.write(struct.pack('<IIII', addr, length, 32, 64))
-        data = ''
-        while True:
-            p = self._esp.read()
-            data += p
-            self._esp.write(struct.pack('<I', len(data)))
-            if show_progress and (len(data) % 1024 == 0 or len(data) == length):
-                progress = '%d (%d %%)' % (len(data), len(data) * 100.0 / length)
-                sys.stdout.write(progress + '\b' * len(progress))
-                sys.stdout.flush()
-            if len(data) == length:
-                break
-            if len(data) > length:
-                raise FatalError('Read more than expected')
-        p = self._esp.read()
-        if len(p) != 16:
-            raise FatalError('Expected digest, got: %s' % hexify(p))
-        expected_digest = hexify(p).upper()
-        digest = hashlib.md5(data).hexdigest().upper()
-        print
-        if digest != expected_digest:
-            raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest))
-        p = self._esp.read()
-        if len(p) != 1:
-            raise FatalError('Expected status, got: %s' % hexify(p))
-        status_code = struct.unpack('<B', p)[0]
-        if status_code != 0:
-            raise FatalError('Write failure, status: %x' % status_code)
-        return data
-
-    def flash_digest(self, addr, length, digest_block_size=0):
-        self._esp.write(struct.pack('<B', self.CMD_FLASH_DIGEST))
-        self._esp.write(struct.pack('<III', addr, length, digest_block_size))
-        digests = []
-        while True:
-            p = self._esp.read()
-            if len(p) == 16:
-                digests.append(p)
-            elif len(p) == 1:
-                status_code = struct.unpack('<B', p)[0]
-                if status_code != 0:
-                    raise FatalError('Write failure, status: %x' % status_code)
-                break
-            else:
-                raise FatalError('Unexpected packet: %s' % hexify(p))
-        return digests[-1], digests[:-1]
-
-    def boot_fw(self):
-        self._esp.write(struct.pack('<B', self.CMD_BOOT_FW))
-        p = self._esp.read()
-        if len(p) != 1:
-            raise FatalError('Expected status, got: %s' % hexify(p))
-        status_code = struct.unpack('<B', p)[0]
-        if status_code != 0:
-            raise FatalError('Boot failure, status: %x' % status_code)
-
-
-def slip_reader(port):
-    """Generator to read SLIP packets from a serial port.
-    Yields one full SLIP packet at a time, raises exception on timeout or invalid data.
-
-    Designed to avoid too many calls to serial.read(1), which can bog
-    down on slow systems.
-    """
-    partial_packet = None
-    in_escape = False
-    while True:
-        waiting = port.inWaiting()
-        read_bytes = port.read(1 if waiting == 0 else waiting)
-        if read_bytes == '':
-            raise FatalError("Timed out waiting for packet %s" % ("header" if partial_packet is None else "content"))
-
-        for b in read_bytes:
-            if partial_packet is None:  # waiting for packet header
-                if b == '\xc0':
-                    partial_packet = ""
-                else:
-                    raise FatalError('Invalid head of packet (%r)' % b)
-            elif in_escape:  # part-way through escape sequence
-                in_escape = False
-                if b == '\xdc':
-                    partial_packet += '\xc0'
-                elif b == '\xdd':
-                    partial_packet += '\xdb'
-                else:
-                    raise FatalError('Invalid SLIP escape (%r%r)' % ('\xdb', b))
-            elif b == '\xdb':  # start of escape sequence
-                in_escape = True
-            elif b == '\xc0':  # end of packet
-                yield partial_packet
-                partial_packet = None
-            else:  # normal byte in packet
-                partial_packet += b
-
-
-def arg_auto_int(x):
-    return int(x, 0)
-
-
-def div_roundup(a, b):
-    """ Return a/b rounded up to nearest integer,
-    equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only
-    without possible floating point accuracy errors.
-    """
-    return (int(a) + int(b) - 1) / int(b)
-
-
-def binutils_safe_path(p):
-    """Returns a 'safe' version of path 'p' to pass to binutils
-
-    Only does anything under Cygwin Python, where cygwin paths need to
-    be translated to Windows paths if the binutils wasn't compiled
-    using Cygwin (should also work with binutils compiled using
-    Cygwin, see #73.)
-    """
-    if sys.platform == "cygwin":
-        try:
-            return subprocess.check_output(["cygpath", "-w", p]).rstrip('\n')
-        except subprocess.CalledProcessError:
-            print "WARNING: Failed to call cygpath to sanitise Cygwin path."
-    return p
-
-
-def align_file_position(f, size):
-    """ Align the position in the file to the next block of specified size """
-    align = (size - 1) - (f.tell() % size)
-    f.seek(align, 1)
-
-
-def hexify(s):
-    return ''.join('%02X' % ord(c) for c in s)
-
-
-def unhexify(hs):
-    s = ''
-    for i in range(0, len(hs) - 1, 2):
-        s += chr(int(hs[i] + hs[i + 1], 16))
-    return s
-
-
-class FatalError(RuntimeError):
-    """
-    Wrapper class for runtime errors that aren't caused by internal bugs, but by
-    ESP8266 responses or input content.
-    """
-    def __init__(self, message):
-        RuntimeError.__init__(self, message)
-
-    @staticmethod
-    def WithResult(message, result):
-        """
-        Return a fatal error object that includes the hex values of
-        'result' as a string formatted argument.
-        """
-        return FatalError(message % ", ".join(hex(ord(x)) for x in result))
-
-
-# "Operation" commands, executable at command line. One function each
-#
-# Each function takes either two args (<ESPROM instance>, <args>) or a single <args>
-# argument.
-
-def load_ram(esp, args):
-    image = LoadFirmwareImage(args.filename)
-
-    print 'RAM boot...'
-    for (offset, size, data) in image.segments:
-        print 'Downloading %d bytes at %08x...' % (size, offset),
-        sys.stdout.flush()
-        esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, offset)
-
-        seq = 0
-        while len(data) > 0:
-            esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq)
-            data = data[esp.ESP_RAM_BLOCK:]
-            seq += 1
-        print 'done!'
-
-    print 'All segments done, executing at %08x' % image.entrypoint
-    esp.mem_finish(image.entrypoint)
-
-
-def read_mem(esp, args):
-    print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address))
-
-
-def write_mem(esp, args):
-    esp.write_reg(args.address, args.value, args.mask, 0)
-    print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address)
-
-
-def dump_mem(esp, args):
-    f = file(args.filename, 'wb')
-    for i in xrange(args.size / 4):
-        d = esp.read_reg(args.address + (i * 4))
-        f.write(struct.pack('<I', d))
-        if f.tell() % 1024 == 0:
-            print '\r%d bytes read... (%d %%)' % (f.tell(),
-                                                  f.tell() * 100 / args.size),
-        sys.stdout.flush()
-    print 'Done!'
-
-
-def write_flash(esp, args):
-    flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode]
-    flash_size_freq = {'4m':0x00, '2m':0x10, '8m':0x20, '16m':0x30, '32m':0x40, '16m-c1': 0x50, '32m-c1':0x60, '32m-c2':0x70, '64m':0x80, '128m':0x90}[args.flash_size]
-    flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq]
-    flash_params = struct.pack('BB', flash_mode, flash_size_freq)
-
-    flasher = CesantaFlasher(esp, args.baud)
-
-    for address, argfile in args.addr_filename:
-        image = argfile.read()
-        argfile.seek(0)  # rewind in case we need it again
-        # Fix sflash config data.
-        if address == 0 and image[0] == '\xe9':
-            print 'Flash params set to 0x%02x%02x' % (flash_mode, flash_size_freq)
-            image = image[0:2] + flash_params + image[4:]
-        # Pad to sector size, which is the minimum unit of writing (erasing really).
-        if len(image) % esp.ESP_FLASH_SECTOR != 0:
-            image += '\xff' * (esp.ESP_FLASH_SECTOR - (len(image) % esp.ESP_FLASH_SECTOR))
-        t = time.time()
-        flasher.flash_write(address, image, not args.no_progress)
-        t = time.time() - t
-        print ('\rWrote %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...'
-               % (len(image), address, t, len(image) / t * 8 / 1000))
-    print 'Leaving...'
-    if args.verify:
-        print 'Verifying just-written flash...'
-        _verify_flash(flasher, args, flash_params)
-    flasher.boot_fw()
-
-
-def image_info(args):
-    image = LoadFirmwareImage(args.filename)
-    print('Image version: %d' % image.version)
-    print('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set'
-    print '%d segments' % len(image.segments)
-    print
-    checksum = ESPROM.ESP_CHECKSUM_MAGIC
-    for (idx, (offset, size, data)) in enumerate(image.segments):
-        if image.version == 2 and idx == 0:
-            print 'Segment 1: %d bytes IROM0 (no load address)' % size
-        else:
-            print 'Segment %d: %5d bytes at %08x' % (idx + 1, size, offset)
-            checksum = ESPROM.checksum(data, checksum)
-    print
-    print 'Checksum: %02x (%s)' % (image.checksum, 'valid' if image.checksum == checksum else 'invalid!')
-
-
-def make_image(args):
-    image = ESPFirmwareImage()
-    if len(args.segfile) == 0:
-        raise FatalError('No segments specified')
-    if len(args.segfile) != len(args.segaddr):
-        raise FatalError('Number of specified files does not match number of specified addresses')
-    for (seg, addr) in zip(args.segfile, args.segaddr):
-        data = file(seg, 'rb').read()
-        image.add_segment(addr, data)
-    image.entrypoint = args.entrypoint
-    image.save(args.output)
-
-
-def elf2image(args):
-    e = ELFFile(args.input)
-    if args.version == '1':
-        image = ESPFirmwareImage()
-    else:
-        image = OTAFirmwareImage()
-        irom_data = e.load_section('.irom0.text')
-        if len(irom_data) == 0:
-            raise FatalError(".irom0.text section not found in ELF file - can't create V2 image.")
-        image.add_segment(0, irom_data, 16)
-    image.entrypoint = e.get_entry_point()
-    for section, start in ((".text", "_text_start"), (".data", "_data_start"), (".rodata", "_rodata_start")):
-        data = e.load_section(section)
-        image.add_segment(e.get_symbol_addr(start), data)
-
-    image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode]
-    image.flash_size_freq = {'4m':0x00, '2m':0x10, '8m':0x20, '16m':0x30, '32m':0x40, '16m-c1': 0x50, '32m-c1':0x60, '32m-c2':0x70, '64m':0x80, '128m':0x90}[args.flash_size]
-    image.flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq]
-
-    irom_offs = e.get_symbol_addr("_irom0_text_start") - 0x40200000
-
-    if args.version == '1':
-        if args.output is None:
-            args.output = args.input + '-'
-        image.save(args.output + "0x00000.bin")
-        data = e.load_section(".irom0.text")
-        if irom_offs < 0:
-            raise FatalError('Address of symbol _irom0_text_start in ELF is located before flash mapping address. Bad linker script?')
-        if (irom_offs & 0xFFF) != 0:  # irom0 isn't flash sector aligned
-            print "WARNING: irom0 section offset is 0x%08x. ELF is probably linked for 'elf2image --version=2'" % irom_offs
-        with open(args.output + "0x%05x.bin" % irom_offs, "wb") as f:
-            f.write(data)
-            f.close()
-    else:  # V2 OTA image
-        if args.output is None:
-            args.output = "%s-0x%05x.bin" % (os.path.splitext(args.input)[0], irom_offs & ~(ESPROM.ESP_FLASH_SECTOR - 1))
-        image.save(args.output)
-
-
-def read_mac(esp, args):
-    mac = esp.read_mac()
-    print 'MAC: %s' % ':'.join(map(lambda x: '%02x' % x, mac))
-
-
-def chip_id(esp, args):
-    chipid = esp.chip_id()
-    print 'Chip ID: 0x%08x' % chipid
-
-
-def erase_flash(esp, args):
-    print 'Erasing flash (this may take a while)...'
-    esp.flash_erase()
-
-
-def run(esp, args):
-    esp.run()
-
-
-def flash_id(esp, args):
-    flash_id = esp.flash_id()
-    print 'Manufacturer: %02x' % (flash_id & 0xff)
-    print 'Device: %02x%02x' % ((flash_id >> 8) & 0xff, (flash_id >> 16) & 0xff)
-
-
-def read_flash(esp, args):
-    flasher = CesantaFlasher(esp, args.baud)
-    t = time.time()
-    data = flasher.flash_read(args.address, args.size, not args.no_progress)
-    t = time.time() - t
-    print ('\rRead %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...'
-           % (len(data), args.address, t, len(data) / t * 8 / 1000))
-    file(args.filename, 'wb').write(data)
-
-
-def _verify_flash(flasher, args, flash_params=None):
-    differences = False
-    for address, argfile in args.addr_filename:
-        image = argfile.read()
-        argfile.seek(0)  # rewind in case we need it again
-        if address == 0 and image[0] == '\xe9' and flash_params is not None:
-            image = image[0:2] + flash_params + image[4:]
-        image_size = len(image)
-        print 'Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name)
-        # Try digest first, only read if there are differences.
-        digest, _ = flasher.flash_digest(address, image_size)
-        digest = hexify(digest).upper()
-        expected_digest = hashlib.md5(image).hexdigest().upper()
-        if digest == expected_digest:
-            print '-- verify OK (digest matched)'
-            continue
-        else:
-            differences = True
-            if getattr(args, 'diff', 'no') != 'yes':
-                print '-- verify FAILED (digest mismatch)'
-                continue
-
-        flash = flasher.flash_read(address, image_size)
-        assert flash != image
-        diff = [i for i in xrange(image_size) if flash[i] != image[i]]
-        print '-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0])
-        for d in diff:
-            print '   %08x %02x %02x' % (address + d, ord(flash[d]), ord(image[d]))
-    if differences:
-        raise FatalError("Verify failed.")
-
-
-def verify_flash(esp, args, flash_params=None):
-    flasher = CesantaFlasher(esp)
-    _verify_flash(flasher, args, flash_params)
-
-
-def version(args):
-    print __version__
-
-#
-# End of operations functions
-#
-
-
-def main():
-    parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool')
-
-    parser.add_argument(
-        '--port', '-p',
-        help='Serial port device',
-        default=os.environ.get('ESPTOOL_PORT', '/dev/ttyUSB0'))
-
-    parser.add_argument(
-        '--baud', '-b',
-        help='Serial port baud rate used when flashing/reading',
-        type=arg_auto_int,
-        default=os.environ.get('ESPTOOL_BAUD', ESPROM.ESP_ROM_BAUD))
-
-    subparsers = parser.add_subparsers(
-        dest='operation',
-        help='Run esptool {command} -h for additional help')
-
-    parser_load_ram = subparsers.add_parser(
-        'load_ram',
-        help='Download an image to RAM and execute')
-    parser_load_ram.add_argument('filename', help='Firmware image')
-
-    parser_dump_mem = subparsers.add_parser(
-        'dump_mem',
-        help='Dump arbitrary memory to disk')
-    parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int)
-    parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int)
-    parser_dump_mem.add_argument('filename', help='Name of binary dump')
-
-    parser_read_mem = subparsers.add_parser(
-        'read_mem',
-        help='Read arbitrary memory location')
-    parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int)
-
-    parser_write_mem = subparsers.add_parser(
-        'write_mem',
-        help='Read-modify-write to arbitrary memory location')
-    parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int)
-    parser_write_mem.add_argument('value', help='Value', type=arg_auto_int)
-    parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int)
-
-    def add_spi_flash_subparsers(parent):
-        """ Add common parser arguments for SPI flash properties """
-        parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency',
-                            choices=['40m', '26m', '20m', '80m'],
-                            default=os.environ.get('ESPTOOL_FF', '40m'))
-        parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode',
-                            choices=['qio', 'qout', 'dio', 'dout'],
-                            default=os.environ.get('ESPTOOL_FM', 'qio'))
-        parent.add_argument('--flash_size', '-fs', help='SPI Flash size in Mbit', type=str.lower,
-                            choices=['4m', '2m', '8m', '16m', '32m', '16m-c1', '32m-c1', '32m-c2', '64m', '128m'],
-                            default=os.environ.get('ESPTOOL_FS', '4m'))
-
-    parser_write_flash = subparsers.add_parser(
-        'write_flash',
-        help='Write a binary blob to flash')
-    parser_write_flash.add_argument('addr_filename', metavar='<address> <filename>', help='Address followed by binary filename, separated by space',
-                                    action=AddrFilenamePairAction)
-    add_spi_flash_subparsers(parser_write_flash)
-    parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true")
-    parser_write_flash.add_argument('--verify', help='Verify just-written data (only necessary if very cautious, data is already CRCed', action='store_true')
-
-    subparsers.add_parser(
-        'run',
-        help='Run application code in flash')
-
-    parser_image_info = subparsers.add_parser(
-        'image_info',
-        help='Dump headers from an application image')
-    parser_image_info.add_argument('filename', help='Image file to parse')
-
-    parser_make_image = subparsers.add_parser(
-        'make_image',
-        help='Create an application image from binary files')
-    parser_make_image.add_argument('output', help='Output image file')
-    parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file')
-    parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int)
-    parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0)
-
-    parser_elf2image = subparsers.add_parser(
-        'elf2image',
-        help='Create an application image from ELF file')
-    parser_elf2image.add_argument('input', help='Input ELF file')
-    parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str)
-    parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1','2'], default='1')
-    add_spi_flash_subparsers(parser_elf2image)
-
-    subparsers.add_parser(
-        'read_mac',
-        help='Read MAC address from OTP ROM')
-
-    subparsers.add_parser(
-        'chip_id',
-        help='Read Chip ID from OTP ROM')
-
-    subparsers.add_parser(
-        'flash_id',
-        help='Read SPI flash manufacturer and device ID')
-
-    parser_read_flash = subparsers.add_parser(
-        'read_flash',
-        help='Read SPI flash content')
-    parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int)
-    parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int)
-    parser_read_flash.add_argument('filename', help='Name of binary dump')
-    parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true")
-
-    parser_verify_flash = subparsers.add_parser(
-        'verify_flash',
-        help='Verify a binary blob against flash')
-    parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space',
-                                     action=AddrFilenamePairAction)
-    parser_verify_flash.add_argument('--diff', '-d', help='Show differences',
-                                     choices=['no', 'yes'], default='no')
-
-    subparsers.add_parser(
-        'erase_flash',
-        help='Perform Chip Erase on SPI flash')
-
-    subparsers.add_parser(
-        'version', help='Print esptool version')
-
-    # internal sanity check - every operation matches a module function of the same name
-    for operation in subparsers.choices.keys():
-        assert operation in globals(), "%s should be a module function" % operation
-
-    args = parser.parse_args()
-
-    print 'esptool.py v%s' % __version__
-
-    # operation function can take 1 arg (args), 2 args (esp, arg)
-    # or be a member function of the ESPROM class.
-
-    operation_func = globals()[args.operation]
-    operation_args,_,_,_ = inspect.getargspec(operation_func)
-    if operation_args[0] == 'esp':  # operation function takes an ESPROM connection object
-        initial_baud = min(ESPROM.ESP_ROM_BAUD, args.baud)  # don't sync faster than the default baud rate
-        esp = ESPROM(args.port, initial_baud)
-        esp.connect()
-        operation_func(esp, args)
-    else:
-        operation_func(args)
-
-
-class AddrFilenamePairAction(argparse.Action):
-    """ Custom parser class for the address/filename pairs passed as arguments """
-    def __init__(self, option_strings, dest, nargs='+', **kwargs):
-        super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs)
-
-    def __call__(self, parser, namespace, values, option_string=None):
-        # validate pair arguments
-        pairs = []
-        for i in range(0,len(values),2):
-            try:
-                address = int(values[i],0)
-            except ValueError as e:
-                raise argparse.ArgumentError(self,'Address "%s" must be a number' % values[i])
-            try:
-                argfile = open(values[i + 1], 'rb')
-            except IOError as e:
-                raise argparse.ArgumentError(self, e)
-            except IndexError:
-                raise argparse.ArgumentError(self,'Must be pairs of an address and the binary filename to write there')
-            pairs.append((address, argfile))
-        setattr(namespace, self.dest, pairs)
-
-# This is "wrapped" stub_flasher.c, to  be loaded using run_stub.
-_CESANTA_FLASHER_STUB = """\
-{"code_start": 1074790404, "code": "080000601C000060000000601000006031FCFF71FCFF\
-81FCFFC02000680332D218C020004807404074DCC48608005823C0200098081BA5A9239245005803\
-1B555903582337350129230B446604DFC6F3FF21EEFFC0200069020DF0000000010078480040004A\
-0040B449004012C1F0C921D911E901DD0209312020B4ED033C2C56C2073020B43C3C56420701F5FF\
-C000003C4C569206CD0EEADD860300202C4101F1FFC0000056A204C2DCF0C02DC0CC6CCAE2D1EAFF\
-0606002030F456D3FD86FBFF00002020F501E8FFC00000EC82D0CCC0C02EC0C73DEB2ADC46030020\
-2C4101E1FFC00000DC42C2DCF0C02DC056BCFEC602003C5C8601003C6C4600003C7C08312D0CD811\
-C821E80112C1100DF0000C180000140010400C0000607418000064180000801800008C1800008418\
-0000881800009018000018980040880F0040A80F0040349800404C4A0040740F0040800F0040980F\
-00400099004012C1E091F5FFC961CD0221EFFFE941F9310971D9519011C01A223902E2D1180C0222\
-6E1D21E4FF31E9FF2AF11A332D0F42630001EAFFC00000C030B43C2256A31621E1FF1A2228022030\
-B43C3256B31501ADFFC00000DD023C4256ED1431D6FF4D010C52D90E192E126E0101DDFFC0000021\
-D2FF32A101C020004802303420C0200039022C0201D7FFC00000463300000031CDFF1A333803D023\
-C03199FF27B31ADC7F31CBFF1A3328030198FFC0000056C20E2193FF2ADD060E000031C6FF1A3328\
-030191FFC0000056820DD2DD10460800000021BEFF1A2228029CE231BCFFC020F51A33290331BBFF\
-C02C411A332903C0F0F4222E1D22D204273D9332A3FFC02000280E27B3F721ABFF381E1A2242A400\
-01B5FFC00000381E2D0C42A40001B3FFC0000056120801B2FFC00000C02000280EC2DC0422D2FCC0\
-2000290E01ADFFC00000222E1D22D204226E1D281E22D204E7B204291E860000126E012198FF32A0\
-042A21C54C003198FF222E1D1A33380337B202C6D6FF2C02019FFFC000002191FF318CFF1A223A31\
-019CFFC00000218DFF1C031A22C549000C02060300003C528601003C624600003C72918BFF9A1108\
-71C861D851E841F83112C1200DF00010000068100000581000007010000074100000781000007C10\
-0000801000001C4B0040803C004091FDFF12C1E061F7FFC961E941F9310971D9519011C01A662906\
-21F3FFC2D1101A22390231F2FF0C0F1A33590331EAFFF26C1AED045C2247B3028636002D0C016DFF\
-C0000021E5FF41EAFF2A611A4469040622000021E4FF1A222802F0D2C0D7BE01DD0E31E0FF4D0D1A\
-3328033D0101E2FFC00000561209D03D2010212001DFFFC000004D0D2D0C3D01015DFFC0000041D5\
-FFDAFF1A444804D0648041D2FF1A4462640061D1FF106680622600673F1331D0FF10338028030C43\
-853A002642164613000041CAFF222C1A1A444804202FC047328006F6FF222C1A273F3861C2FF222C\
-1A1A6668066732B921BDFF3D0C1022800148FFC0000021BAFF1C031A2201BFFFC000000C02460300\
-5C3206020000005C424600005C5291B7FF9A110871C861D851E841F83112C1200DF0B0100000C010\
-0000D010000012C1E091FEFFC961D951E9410971F931CD039011C0ED02DD0431A1FF9C1422A06247\
-B302062D0021F4FF1A22490286010021F1FF1A223902219CFF2AF12D0F011FFFC00000461C0022D1\
-10011CFFC0000021E9FFFD0C1A222802C7B20621E6FF1A22F8022D0E3D014D0F0195FFC000008C52\
-22A063C6180000218BFF3D01102280F04F200111FFC00000AC7D22D1103D014D0F010DFFC0000021\
-D6FF32D110102280010EFFC0000021D3FF1C031A220185FFC00000FAEEF0CCC056ACF821CDFF317A\
-FF1A223A310105FFC0000021C9FF1C031A22017CFFC000002D0C91C8FF9A110871C861D851E841F8\
-3112C1200DF0000200600000001040020060FFFFFF0012C1E00C02290131FAFF21FAFF026107C961\
-C02000226300C02000C80320CC10564CFF21F5FFC02000380221F4FF20231029010C432D010163FF\
-C0000008712D0CC86112C1200DF00080FE3F8449004012C1D0C9A109B17CFC22C1110C13C51C0026\
-1202463000220111C24110B68202462B0031F5FF3022A02802A002002D011C03851A0066820A2801\
-32210105A6FF0607003C12C60500000010212032A01085180066A20F2221003811482105B3FF2241\
-10861A004C1206FDFF2D011C03C5160066B20E280138114821583185CFFF06F7FF005C1286F5FF00\
-10212032A01085140066A20D2221003811482105E1FF06EFFF0022A06146EDFF45F0FFC6EBFF0000\
-01D2FFC0000006E9FF000C022241100C1322C110C50F00220111060600000022C1100C13C50E0022\
-011132C2FA303074B6230206C8FF08B1C8A112C1300DF0000000000010404F484149007519031027\
-000000110040A8100040BC0F0040583F0040CC2E00401CE20040D83900408000004021F4FF12C1E0\
-C961C80221F2FF097129010C02D951C91101F4FFC0000001F3FFC00000AC2C22A3E801F2FFC00000\
-21EAFFC031412A233D0C01EFFFC000003D0222A00001EDFFC00000C1E4FF2D0C01E8FFC000002D01\
-32A004450400C5E7FFDD022D0C01E3FFC00000666D1F4B2131DCFF4600004B22C0200048023794F5\
-31D9FFC0200039023DF08601000001DCFFC000000871C861D85112C1200DF000000012C1F0026103\
-01EAFEC00000083112C1100DF000643B004012C1D0E98109B1C9A1D991F97129013911E2A0C001FA\
-FFC00000CD02E792F40C0DE2A0C0F2A0DB860D00000001F4FFC00000204220E71240F7921C226102\
-01EFFFC0000052A0DC482157120952A0DD571205460500004D0C3801DA234242001BDD3811379DC5\
-C6000000000C0DC2A0C001E3FFC00000C792F608B12D0DC8A1D891E881F87112C1300DF00000", "\
-entry": 1074792180, "num_params": 1, "params_start": 1074790400, "data": "FE0510\
-401A0610403B0610405A0610407A061040820610408C0610408C061040", "data_start": 10736\
-43520}
-"""
-
-if __name__ == '__main__':
-    try:
-        main()
-    except FatalError as e:
-        print '\nA fatal error occurred: %s' % e
-        sys.exit(2)

+ 286 - 0
tools/nodemcu-partition.py

@@ -0,0 +1,286 @@
+#!/usr/bin/env python
+#
+# ESP8266 LFS Loader Utility
+#
+# Copyright (C) 2019 Terry Ellison, NodeMCU Firmware Community Project. drawing
+# heavily from and including content from esptool.py with full acknowledgement
+# under GPL 2.0, with said content: Copyright (C) 2014-2016 Fredrik Ahlberg, Angus
+# Gratton, Espressif Systems  (Shanghai) PTE LTD, other contributors as noted.
+# https://github.com/espressif/esptool
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+# Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import os
+import sys
+sys.path.append(os.path.realpath(os.path.dirname(__file__) + '/toolchains/'))
+import esptool
+
+import io
+import tempfile
+import shutil
+
+from pprint import pprint
+
+import argparse
+import gzip
+import copy
+import inspect
+import struct
+import string
+
+__version__     = '1.0'
+__program__     = 'nodemcu-partition.py'
+ROM0_Seg        =   0x010000
+FLASH_PAGESIZE  =   0x001000
+FLASH_BASE_ADDR = 0x40200000
+PARTITION_TYPES = {
+       4: 'RF_CAL',
+       5: 'PHY_DATA',
+       6: 'SYSTEM_PARAMETER',
+     101: 'EAGLEROM',
+     102: 'IROM0TEXT',
+     103: 'LFS0',
+     104: 'LFS1',
+     105: 'TLSCERT',
+     106: 'SPIFFS0',
+     107: 'SPIFFS1'}
+
+MAX_PT_SIZE = 20*3
+FLASH_SIG          = 0xfafaa150
+FLASH_SIG_MASK     = 0xfffffff0
+FLASH_SIG_ABSOLUTE = 0x00000001
+WORDSIZE           = 4
+WORDBITS           = 32
+
+PACK_INT    = struct.Struct("<I")
+
+class FatalError(RuntimeError):
+    def __init__(self, message):
+        RuntimeError.__init__(self, message)
+
+    def WithResult(message, result):
+        message += " (result was %s)" % hexify(result)
+        return FatalError(message)
+
+def load_PT(data, args):
+    """
+    Load the Flash copy of the Partition Table from the first segment of the IROM0
+    segment, that is at 0x10000.  If nececessary the LFS partition is then correctly
+    positioned and adjusted according to the optional start and len arguments.
+
+    The (possibly) updated PT is then returned with the LFS sizing.
+    """
+    pt = [PACK_INT.unpack_from(data,4*i)[0] for i in range(0, MAX_PT_SIZE)]
+    n, flash_used_end, rewrite = 0, 0, False
+    LFSaddr, LFSsize = None, None
+
+    # The partition table format is a set of 3*uint32 fields (type, addr, size), 
+    # with the last slot being an end marker (0,size,0) where size is the size of 
+    # the firmware image.
+
+    pt_map = dict()
+    for i in range(0,MAX_PT_SIZE,3):
+        if pt[i] == 0:
+            n = i // 3
+            break
+        elif pt[i] in PARTITION_TYPES:
+            pt_map[PARTITION_TYPES[pt[i]]] = i       
+        else:
+            raise FatalError("Unknown partition type: %u" % pt[i]) 
+
+    flash_used_end = pt[3*n+1]
+ 
+    if not ('IROM0TEXT' in pt_map and 'LFS0' in pt_map):
+        raise FatalError("Partition table must contain IROM0 and LFS segments")
+
+    i = pt_map['IROM0TEXT']
+    if pt[i+2] == 0:
+        pt[i+2] = (flash_used_end - FLASH_BASE_ADDR) - pt[i+1]
+
+    j = pt_map['LFS0']
+    if args.la is not None: 
+        pt[j+1] = args.la
+    elif pt[j+1] == 0:
+        pt[j+1] = pt[i+1] + pt[i+2]
+
+    if args.ls is not None:
+        pt[j+2] = args.ls
+    elif pt[j+2] == 0:
+        pt[j+2] = 0x10000
+
+    k = pt_map['SPIFFS0']
+    if args.sa is not None: 
+        pt[k+1] = args.sa
+    elif pt[k+1] == 0:
+        pt[k+1] = pt[j+1] + pt[j+2]
+
+    if args.ss is not None:
+        pt[k+2] = args.ss
+
+    LFSaddr, LFSsize = pt[j+1], pt[j+2]
+    print ('\nDump of Partition Table\n')
+
+    for i in range(0,3*n,3):
+        print ('%-18s  0x%06x  0x%06x' % (PARTITION_TYPES[pt[i]], pt[i+1], pt[i+2]))
+
+    return pt, pt_map, n
+
+def relocate_lfs(data, addr, size):
+    """
+    The unpacked LFS image comprises the relocatable image itself, followed by a bit
+    map (one bit per word) flagging if the corresponding word of the image needs
+    relocating.  The image and bitmap are enumerated with any addresses being
+    relocated by the LFS base address.  (Note that the PIC format of addresses is word
+    aligned and so first needs scaling by the wordsize.)
+    """
+    addr += FLASH_BASE_ADDR
+    w = [PACK_INT.unpack_from(data,WORDSIZE*i)[0] \
+            for i in range(0, len(data) // WORDSIZE)]
+    flash_sig, flash_size = w[0], w[1]
+
+    assert ((flash_sig & FLASH_SIG_MASK) == FLASH_SIG and
+            (flash_sig & FLASH_SIG_ABSOLUTE) == 0 and
+             flash_size % WORDSIZE == 0)
+
+    flash_size //= WORDSIZE
+    flags_size = (flash_size + WORDBITS - 1) // WORDBITS
+
+    assert (WORDSIZE*flash_size <= size and
+            len(data) == WORDSIZE*(flash_size + flags_size))
+
+    image,flags,j    = w[0:flash_size], w[flash_size:], 0
+
+    for i in range(0,len(image)):
+        if i % WORDBITS == 0:
+            flag_word = flags[j]
+            j += 1
+        if (flag_word & 1) == 1:
+            o = image[i]
+            image[i] = WORDSIZE*image[i] + addr
+        flag_word >>= 1
+
+    return ''.join([PACK_INT.pack(i) for i in image])
+
+def main():
+
+    def arg_auto_int(x):
+        ux = x.upper()
+        if "MB" in ux:
+            return int(ux[:ux.index("MB")]) * 1024 * 1024
+        elif "KB" in ux:
+            return int(ux[:ux.index("KB")]) * 1024
+        else:
+            return int(ux, 0)
+
+    print('%s V%s' %(__program__, __version__))
+
+    # ---------- process the arguments ---------- #
+
+    a = argparse.ArgumentParser(
+        description='%s V%s - ESP8266 NodeMCU Loader Utility' % 
+                     (__program__, __version__),
+        prog='esplfs')
+    a.add_argument('--port', '-p', help='Serial port device')
+    a.add_argument('--baud', '-b',  type=arg_auto_int,
+        help='Serial port baud rate used when flashing/reading')
+    a.add_argument('--lfs-addr', '-la', dest="la", type=arg_auto_int,
+        help='(Overwrite) start address of LFS partition')
+    a.add_argument('--lfs-size', '-ls', dest="ls", type=arg_auto_int,
+        help='(Overwrite) length of LFS partition')
+    a.add_argument('--lfs-file', '-lf', dest="lf", help='LFS image file')
+    a.add_argument('--spiffs-addr', '-sa', dest="sa", type=arg_auto_int,
+        help='(Overwrite) start address of SPIFFS partition')
+    a.add_argument('--spiffs-size', '-ss', dest="ss", type=arg_auto_int,
+        help='(Overwrite) length of SPIFFS partition')
+    a.add_argument('--spiffs-file', '-sf', dest="sf", help='SPIFFS image file')
+
+    arg = a.parse_args()
+
+    if arg.lf is not None:
+        if not os.path.exists(arg.lf):
+            raise FatalError("LFS image %s does not exist" % arg.lf)
+
+    if arg.sf is not None:
+        if not os.path.exists(arg.sf):
+           raise FatalError("SPIFFS image %s does not exist" % arg.sf)
+
+    base = [] if arg.port is None else ['--port',arg.port]
+    if arg.baud is not None: base.extend(['--baud',arg.baud])
+
+    # ---------- Use esptool to read the PT ---------- #
+
+    tmpdir  = tempfile.mkdtemp()
+    pt_file = tmpdir + '/pt.dmp'
+    espargs = base+['--after', 'no_reset', 'read_flash', '--no-progress',
+                    str(ROM0_Seg), str(FLASH_PAGESIZE), pt_file]
+    esptool.main(espargs)
+
+    with open(pt_file,"rb") as f:
+        data = f.read()
+
+    pt, pt_map, n = load_PT(data, arg)
+    n = n+1
+
+    odata = ''.join([PACK_INT.pack(pt[i]) for i in range(0,3*n)]) + \
+            "\xFF" * len(data[3*4*n:])
+
+    # ---------- If the PT has changed then use esptool to rewrite it ---------- #
+
+    if odata != data:
+        print("PT updated")
+        pt_file = tmpdir + '/opt.dmp'
+        with open(pt_file,"wb") as f:
+            f.write(odata)
+        espargs = base+['--after', 'no_reset', 'write_flash', '--no-progress',
+                        str(ROM0_Seg), pt_file]
+        esptool.main(espargs)
+
+    if arg.lf is not None:
+        i     = pt_map['LFS0']
+        la,ls = pt[i+1], pt[i+2]
+
+        # ---------- Read and relocate the LFS image ---------- #
+    
+        with gzip.open(arg.lf) as f:
+            lfs = f.read()
+            lfs = relocate_lfs(lfs, la, ls)
+
+        # ---------- Write to a temp file and use esptool to write it to flash ---------- #
+
+        img_file = tmpdir + '/lfs.img'
+        espargs = base + ['write_flash', str(la), img_file]
+        with open(img_file,"wb") as f:
+            f.write(lfs)
+        esptool.main(espargs)
+
+    if arg.sf is not None:
+        sa = pt[pt_map['SPIFFS0']+1]
+
+        # ---------- Write to a temp file and use esptool to write it to flash ---------- #
+
+        spiffs_file = arg.sf
+        espargs = base + ['', str(sa), spiffs_file]
+        esptool.main(espargs)
+
+    # ---------- Clean up temp directory ---------- #
+
+    espargs = base + ['--after', 'hard_reset', 'flash_id']
+    esptool.main(espargs)
+
+    shutil.rmtree(tmpdir)
+
+def _main():
+        main()
+
+if __name__ == '__main__':
+    _main()

Някои файлове не бяха показани, защото твърде много файлове са промени