1st Tranch of SDK 3.0 follow up changes (#2732)

1st Tranche of SDK 3.0 follow up changes
This commit is contained in:
Terry Ellison 2019-05-01 18:29:11 +01:00 committed by GitHub
parent b7a99358cc
commit bc61528db7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 921 additions and 324 deletions

229
Makefile
View File

@ -2,21 +2,32 @@
# #
.NOTPARALLEL: .NOTPARALLEL:
TOOLCHAIN_VERSION:=20181106.0
# SDK base version, as released by Espressif
SDK_BASE_VER:=3.0
SDK_VER:=$(SDK_BASE_VER)
SDK_DIR_DEPENDS:=sdk_extracted
SDK_FILE_VER:=$(SDK_BASE_VER)
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)))) TOP_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST))))
SDK_REL_DIR=sdk/esp_iot_sdk_v$(SDK_VER)
SDK_DIR:=$(TOP_DIR)/$(SDK_REL_DIR) # SDK base version, as released by Espressif depends on the RELEASE flag
#
# RELEASE = lastest pulls the latest V3.0.0 branch version as at the issue of this make
# otherwise it pulls the labelled version in the SDK version's release directory
#
ifeq ("$(RELEASE)","latest")
export RELEASE:=$(RELEASE)
SDK_VER := 3.0.0-dev-190412
SDK_COMMIT_SHA1:= 39ec2d4573eb77fda73f6afcf6dd1b3c41e74fcd
SDK_FILE_SHA1 := 44f7724490739536526fc4298d6fcc2fa2d29471
SDK_ZIP_ROOT := ESP8266_NONOS_SDK-$(SDK_COMMIT_SHA1)
SDK_FILE_VER := $(SDK_COMMIT_SHA1)
else
SDK_VER := 3.0
SDK_FILE_SHA1 := 029fc23fe87e03c9852de636490b2d7b9e07f01a
SDK_ZIP_ROOT := ESP8266_NONOS_SDK-$(SDK_VER)
SDK_FILE_VER := v$(SDK_VER)
endif
SDK_REL_DIR := sdk/esp_iot_sdk_v$(SDK_VER)
SDK_DIR := $(TOP_DIR)/$(SDK_REL_DIR)
ESPTOOL_VER := 2.6
# Ensure that the Espresif SDK is searched before the tool-chain's SDK (if any)
CCFLAGS:= -I$(TOP_DIR)/sdk-overrides/include -I$(TOP_DIR)/app/include/lwip/app -I$(SDK_DIR)/include CCFLAGS:= -I$(TOP_DIR)/sdk-overrides/include -I$(TOP_DIR)/app/include/lwip/app -I$(SDK_DIR)/include
LDFLAGS:= -L$(SDK_DIR)/lib -L$(SDK_DIR)/ld $(LDFLAGS) LDFLAGS:= -L$(SDK_DIR)/lib -L$(SDK_DIR)/ld $(LDFLAGS)
@ -27,19 +38,19 @@ else
CCFLAGS += -O2 CCFLAGS += -O2
endif endif
#Handling of V=1/VERBOSE=1 flag
# Handling of V=1/VERBOSE=1 flag
# #
# if V=1, $(summary) does nothing # if V=1, $(summary) does nothing
# if V is unset or not 1, $(summary) echoes a summary # if V is unset or not 1, $(summary) echoes a summary
VERBOSE ?= VERBOSE ?=
V ?= $(VERBOSE) V ?= $(VERBOSE)
ifeq ("$(V)","1") ifeq ("$(V)","1")
export summary := @true export summary := @true
else else
export summary := @echo export summary := @echo
# disable echoing of commands, directory names
# disable echoing of commands, directory names MAKEFLAGS += --silent -w
MAKEFLAGS += --silent -w
endif # $(V)==1 endif # $(V)==1
ifndef BAUDRATE ifndef BAUDRATE
@ -49,9 +60,34 @@ endif
############################################################# #############################################################
# Select compile # Select compile
# #
ifeq ($(OS),Windows_NT) # ** HEALTH WARNING ** This section is largely legacy directives left over from
# WIN32 # an Espressif template. As far as I (TerrryE) know, we've only used the Linux
# We are under windows. # Path. I have successfully build AMD and Intel (both x86, AMD64) and RPi ARM6
# all under Ubuntu. Our docker container runs on Windows in an Ubuntu VM.
# Johny Mattson maintains a prebuild AMD64 xtensa cross-compile gcc v4.8.5
# toolchain which is compatible with the non-OS SDK and can be used on any recent
# Ubuntu version including the Docker and Travis build environments.
#
# You have the option to build your own toolchain and specify a TOOLCHAIN_ROOT
# environment variable (see https://github.com/pfalcon/esp-open-sdk). If your
# architecture is compatable then you can omit this variable and the make will
# download and use this prebuilt toolchain.
#
# If any developers wish to develop, test and support alternative environments
# then please raise a GitHub issue on this work.
#
ifndef $(OS)
# Assume Windows if MAKE_HOST contains "indows" and Linux otherwise
ifneq (,$(findstring indows,$(MAKE_HOST)))
OS := windows
else
OS := linux
endif
endif
ifneq (,$(findstring indows,$(OS)))
#------------ BEGIN UNTESTED ------------ We are not under Linux, e.g.under windows.
ifeq ($(XTENSA_CORE),lx106) ifeq ($(XTENSA_CORE),lx106)
# It is xcc # It is xcc
AR = xt-ar AR = xt-ar
@ -85,58 +121,54 @@ ifeq ($(OS),Windows_NT)
ifeq ($(PROCESSOR_ARCHITECTURE),x86) ifeq ($(PROCESSOR_ARCHITECTURE),x86)
# ->IA32 # ->IA32
endif endif
#---------------- END UNTESTED ---------------- We are under windows.
else else
# We are under other system, may be Linux. Assume using gcc. # We are under other system, may be Linux. Assume using gcc.
# Can we use -fdata-sections?
PLATFORM:=linux-x86_64 UNAME_S := $(shell uname -s)
UNAME_P := $(shell uname -p)
ifeq ($(OS),linux)
ifndef TOOLCHAIN_ROOT
TOOLCHAIN_VERSION = 20181106.0
GCCTOOLCHAIN = linux-x86_64-$(TOOLCHAIN_VERSION)
TOOLCHAIN_ROOT = $(TOP_DIR)/tools/toolchains/esp8266-$(GCCTOOLCHAIN)
GITHUB_TOOLCHAIN = https://github.com/jmattsson/esp-toolchains
export PATH:=$(PATH):$(TOOLCHAIN_ROOT)/bin
endif
endif
ifndef COMPORT ifndef COMPORT
ESPPORT = /dev/ttyUSB0 ESPPORT = /dev/ttyUSB0
else else
ESPPORT = $(COMPORT) ESPPORT = $(COMPORT)
endif endif
export PATH := $(PATH):$(TOP_DIR)/tools/toolchains/esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION)/bin/
CCFLAGS += -ffunction-sections -fno-jump-tables -fdata-sections CCFLAGS += -ffunction-sections -fno-jump-tables -fdata-sections
AR = xtensa-lx106-elf-ar AR = xtensa-lx106-elf-ar
CC = $(WRAPCC) xtensa-lx106-elf-gcc CC = $(WRAPCC) xtensa-lx106-elf-gcc
CXX = $(WRAPCC) xtensa-lx106-elf-g++ CXX = $(WRAPCC) xtensa-lx106-elf-g++
NM = xtensa-lx106-elf-nm NM = xtensa-lx106-elf-nm
CPP = $(WRAPCC) xtensa-lx106-elf-gcc -E CPP = $(WRAPCC) xtensa-lx106-elf-gcc -E
OBJCOPY = xtensa-lx106-elf-objcopy OBJCOPY = xtensa-lx106-elf-objcopy
FIRMWAREDIR = ../bin/ FIRMWAREDIR = ../bin/
WGET = wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused WGET = wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
# LINUX
endif
ifeq ($(UNAME_S),Darwin)
# OSX
endif
UNAME_P := $(shell uname -p)
ifeq ($(UNAME_P),x86_64)
# ->AMD64
endif
ifneq ($(filter %86,$(UNAME_P)),)
# ->IA32
endif
ifneq ($(filter arm%,$(UNAME_P)),)
# ->ARM
endif
endif endif
#############################################################
GITHUB_TOOLCHAIN = https://github.com/jmattsson/esp-toolchains
GITHUB_SDK = https://github.com/espressif/ESP8266_NONOS_SDK GITHUB_SDK = https://github.com/espressif/ESP8266_NONOS_SDK
GITHUB_ESPTOOL = https://github.com/espressif/esptool GITHUB_ESPTOOL = https://github.com/espressif/esptool
ESPTOOL ?= $(TOP_DIR)/tools/toolchains/esptool.py ESPTOOL ?= $(TOP_DIR)/tools/toolchains/esptool.py
CSRCS ?= $(wildcard *.c)
CXXSRCS ?= $(wildcard *.cpp)
ASRCs ?= $(wildcard *.s)
ASRCS ?= $(wildcard *.S)
SUBDIRS ?= $(patsubst %/,%,$(dir $(filter-out tools/Makefile,$(wildcard */Makefile)))) SUBDIRS ?= $(patsubst %/,%,$(dir $(filter-out tools/Makefile,$(wildcard */Makefile))))
ODIR := .output ODIR := .output
ifdef TARGET
CSRCS ?= $(wildcard *.c)
CXXSRCS ?= $(wildcard *.cpp)
ASRCs ?= $(wildcard *.s)
ASRCS ?= $(wildcard *.S)
OBJODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/obj OBJODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/obj
OBJS := $(CSRCS:%.c=$(OBJODIR)/%.o) \ OBJS := $(CSRCS:%.c=$(OBJODIR)/%.o) \
@ -159,19 +191,19 @@ BINODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/bin
OBINS := $(GEN_BINS:%=$(BINODIR)/%) OBINS := $(GEN_BINS:%=$(BINODIR)/%)
ifndef PDIR ifndef PDIR
ifneq ($(wildcard $(TOP_DIR)/local/fs/*),) ifneq ($(wildcard $(TOP_DIR)/local/fs/*),)
SPECIAL_MKTARGETS += spiffs-image SPECIAL_MKTARGETS += spiffs-image
else else
SPECIAL_MKTARGETS += spiffs-image-remove SPECIAL_MKTARGETS += spiffs-image-remove
endif
endif endif
endif endif # TARGET
# #
# Note: # Note:
# https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html # https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
# If you add global optimize options like "-O2" here # If you add global optimize options then they will override "-Os" defined above.
# they will override "-Os" defined above. # Note that "-Os" should NOT be used to reduce code size because of the runtime
# "-Os" should be used to reduce code size # impact of the extra non-aligned exception burdon.
# #
CCFLAGS += \ CCFLAGS += \
-g \ -g \
@ -193,6 +225,8 @@ DFLAGS = $(CCFLAGS) $(DDEFINES) $(EXTRA_CCFLAGS) $(STD_CFLAGS) $(INCLUDES)
# Functions # Functions
# #
ifdef TARGET
define ShortcutRule define ShortcutRule
$(1): .subdirs $(2)/$(1) $(1): .subdirs $(2)/$(1)
endef endef
@ -225,39 +259,46 @@ $(BINODIR)/%.bin: $(IMAGEODIR)/%.out
$(summary) ESPTOOL $(patsubst $(TOP_DIR)/%,%,$(CURDIR))/$< $(FIRMWAREDIR) $(summary) ESPTOOL $(patsubst $(TOP_DIR)/%,%,$(CURDIR))/$< $(FIRMWAREDIR)
$(ESPTOOL) elf2image --flash_mode dio --flash_freq 40m $< -o $(FIRMWAREDIR) $(ESPTOOL) elf2image --flash_mode dio --flash_freq 40m $< -o $(FIRMWAREDIR)
endif # TARGET
############################################################# #############################################################
# Rules base # Rules base
# Should be done in top-level makefile only # Should be done in top-level makefile only
# #
all: toolchain sdk_pruned pre_build .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS) ifndef TARGET
all: toolchain sdk_pruned pre_build .subdirs
else
all: .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
endif
.PHONY: sdk_extracted .PHONY: sdk_extracted
.PHONY: sdk_pruned .PHONY: sdk_pruned
.PHONY: toolchain .PHONY: toolchain
sdk_extracted: $(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER) sdk_extracted: $(TOP_DIR)/sdk/.extracted-$(SDK_VER)
sdk_pruned: $(SDK_DIR_DEPENDS) $(TOP_DIR)/sdk/.pruned-$(SDK_VER) sdk_pruned: sdk_extracted toolchain $(TOP_DIR)/sdk/.pruned-$(SDK_VER)
ifeq ($(OS),Windows_NT) ifdef GITHUB_TOOLCHAIN
toolchain: TOOLCHAIN_ROOT := $(TOP_DIR)/tools/toolchains/esp8266-linux-x86_64-$(TOOLCHAIN_VERSION)
else
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 toolchain: $(TOOLCHAIN_ROOT)/bin $(ESPTOOL)
$(TOOLCHAIN_ROOT)/bin: $(TOP_DIR)/cache/toolchain-esp8266-$(GCCTOOLCHAIN).tar.xz
mkdir -p $(TOP_DIR)/tools/toolchains/ mkdir -p $(TOP_DIR)/tools/toolchains/
$(summary) EXTRACT $(patsubst $(TOP_DIR)/%,%,$<) $(summary) EXTRACT $(patsubst $(TOP_DIR)/%,%,$<)
tar -xJf $< -C $(TOP_DIR)/tools/toolchains/ tar -xJf $< -C $(TOP_DIR)/tools/toolchains/
touch $@ touch $@
$(TOP_DIR)/cache/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz: $(TOP_DIR)/cache/toolchain-esp8266-$(GCCTOOLCHAIN).tar.xz:
mkdir -p $(TOP_DIR)/cache mkdir -p $(TOP_DIR)/cache
$(summary) WGET $(patsubst $(TOP_DIR)/%,%,$@) $(summary) WGET $(patsubst $(TOP_DIR)/%,%,$@)
$(WGET) $(GITHUB_TOOLCHAIN)/releases/download/$(PLATFORM)-$(TOOLCHAIN_VERSION)/toolchain-esp8266-$(PLATFORM)-$(TOOLCHAIN_VERSION).tar.xz -O $@ \ $(WGET) $(GITHUB_TOOLCHAIN)/releases/download/$(GCCTOOLCHAIN)/toolchain-esp8266-$(GCCTOOLCHAIN).tar.xz -O $@ \
|| { rm -f "$@"; exit 1; } || { rm -f "$@"; exit 1; }
else
toolchain: $(ESPTOOL)
endif endif
$(TOP_DIR)/tools/toolchains/esptool.py: $(TOP_DIR)/cache/esptool/v$(ESPTOOL_VER).tar.gz $(ESPTOOL): $(TOP_DIR)/cache/esptool/v$(ESPTOOL_VER).tar.gz
mkdir -p $(TOP_DIR)/tools/toolchains/ mkdir -p $(TOP_DIR)/tools/toolchains/
tar -C $(TOP_DIR)/tools/toolchains/ -xzf $< --strip-components=1 esptool-$(ESPTOOL_VER)/esptool.py tar -C $(TOP_DIR)/tools/toolchains/ -xzf $< --strip-components=1 esptool-$(ESPTOOL_VER)/esptool.py
chmod +x $@ chmod +x $@
@ -267,31 +308,32 @@ $(TOP_DIR)/cache/esptool/v$(ESPTOOL_VER).tar.gz:
mkdir -p $(TOP_DIR)/cache/esptool/ mkdir -p $(TOP_DIR)/cache/esptool/
$(WGET) $(GITHUB_ESPTOOL)/archive/v$(ESPTOOL_VER).tar.gz -O $@ || { rm -f "$@"; exit 1; } $(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 $(TOP_DIR)/sdk/.extracted-$(SDK_VER): $(TOP_DIR)/cache/$(SDK_FILE_VER).zip
mkdir -p "$(dir $@)" mkdir -p "$(dir $@)"
$(summary) UNZIP $(patsubst $(TOP_DIR)/%,%,$<) $(summary) UNZIP $(patsubst $(TOP_DIR)/%,%,$<)
(cd "$(dir $@)" && \ (cd "$(dir $@)" && \
rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK-$(SDK_BASE_VER) && \ rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK-* && \
unzip $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip \ unzip $(TOP_DIR)/cache/$(SDK_FILE_VER).zip \
'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/lib/*' \ '$(SDK_ZIP_ROOT)/lib/*' \
'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/ld/*.v6.ld' \ '$(SDK_ZIP_ROOT)/ld/*.v6.ld' \
'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/include/*' \ '$(SDK_ZIP_ROOT)/include/*' \
'ESP8266_NONOS_SDK-$(SDK_BASE_VER)/bin/esp_init_data_default_v05.bin' \ '$(SDK_ZIP_ROOT)/bin/esp_init_data_default_v05.bin' \
) )
mv $(dir $@)/ESP8266_NONOS_SDK-$(SDK_BASE_VER) $(dir $@)/esp_iot_sdk_v$(SDK_BASE_VER) mv $(dir $@)/$(SDK_ZIP_ROOT) $(dir $@)/esp_iot_sdk_v$(SDK_VER)
touch $@ touch $@
$(TOP_DIR)/sdk/.pruned-$(SDK_VER): $(TOP_DIR)/sdk/.pruned-$(SDK_VER):
rm -f $(SDK_DIR)/lib/liblwip.a $(SDK_DIR)/lib/libssl.a $(SDK_DIR)/lib/libmbedtls.a rm -f $(SDK_DIR)/lib/liblwip.a $(SDK_DIR)/lib/libssl.a $(SDK_DIR)/lib/libmbedtls.a
$(summary) PRUNE libmain.a libc.a $(summary) PRUNE libmain.a libc.a
echo $(PATH)
$(AR) d $(SDK_DIR)/lib/libmain.a time.o $(AR) d $(SDK_DIR)/lib/libmain.a time.o
$(AR) d $(SDK_DIR)/lib/libc.a lib_a-time.o $(AR) d $(SDK_DIR)/lib/libc.a lib_a-time.o
touch $@ touch $@
$(TOP_DIR)/cache/v$(SDK_FILE_VER).zip: $(TOP_DIR)/cache/$(SDK_FILE_VER).zip:
mkdir -p "$(dir $@)" mkdir -p "$(dir $@)"
$(summary) WGET $(patsubst $(TOP_DIR)/%,%,$@) $(summary) WGET $(patsubst $(TOP_DIR)/%,%,$@)
$(WGET) $(GITHUB_SDK)/archive/v$(SDK_FILE_VER).zip -O $@ || { rm -f "$@"; exit 1; } $(WGET) $(GITHUB_SDK)/archive/$(SDK_FILE_VER).zip -O $@ || { rm -f "$@"; exit 1; }
(echo "$(SDK_FILE_SHA1) $@" | sha1sum -c -) || { rm -f "$@"; exit 1; } (echo "$(SDK_FILE_SHA1) $@" | sha1sum -c -) || { rm -f "$@"; exit 1; }
clean: clean:
@ -329,15 +371,12 @@ endif
.subdirs: .subdirs:
@set -e; $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);) @set -e; $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);)
#.subdirs:
# $(foreach d, $(SUBDIRS), $(MAKE) -C $(d))
ifneq ($(MAKECMDGOALS),clean) ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),clobber) ifneq ($(MAKECMDGOALS),clobber)
ifdef DEPS ifdef DEPS
sinclude $(DEPS) sinclude $(DEPS)
endif endif
endif endif
endif endif
.PHONY: spiffs-image-remove .PHONY: spiffs-image-remove
@ -365,6 +404,7 @@ pre_build:
@-rm -f $(TOP_DIR)/app/modules/server-ca.crt.h @-rm -f $(TOP_DIR)/app/modules/server-ca.crt.h
endif endif
ifdef TARGET
$(OBJODIR)/%.o: %.c $(OBJODIR)/%.o: %.c
@mkdir -p $(dir $@); @mkdir -p $(dir $@);
$(summary) CC $(patsubst $(TOP_DIR)/%,%,$(CURDIR))/$< $(summary) CC $(patsubst $(TOP_DIR)/%,%,$(CURDIR))/$<
@ -424,6 +464,7 @@ $(foreach lib,$(GEN_LIBS),$(eval $(call MakeLibrary,$(basename $(lib)))))
$(foreach image,$(GEN_IMAGES),$(eval $(call MakeImage,$(basename $(image))))) $(foreach image,$(GEN_IMAGES),$(eval $(call MakeImage,$(basename $(image)))))
endif # TARGET
############################################################# #############################################################
# Recursion Magic - Don't touch this!! # Recursion Magic - Don't touch this!!
# #

View File

@ -41,9 +41,12 @@
// The Lua Flash Store (LFS) allows you to store Lua code in Flash memory and // 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 // the Lua VMS will execute this code directly from flash without needing any
// RAM overhead. Note that you should now configure LFS directly in the // RAM overhead. You can now configure LFS directly in the System Partition
// System Partition Table and not at build time. // Table insted of at compile time. However for backwards compatibility setting
// LUA_FLASH_STORE defines the default partition size if the NodeMCU partition
// tool is not used.
//#define LUA_FLASH_STORE 0x10000
// By default Lua executes the file init.lua at start up. The following // By default Lua executes the file init.lua at start up. The following
// define allows you to replace this with an alternative startup. Warning: // define allows you to replace this with an alternative startup. Warning:
@ -68,11 +71,14 @@
// general, limiting the size of the FS only to what your application needs // general, limiting the size of the FS only to what your application needs
// gives the fastest start-up and imaging times. // gives the fastest start-up and imaging times.
// Note that you should now configure SPIFFS size and position directly in the // You can now configure SPIFFS size and position directly in the System
// System Partition Table and not at build time. // Partition Table. However backwards compatibility SPIFFS_MAX_FILESYSTEM_SIZE
// can be set and this defines the default SPIFFS partition size if the NodeMCU
// partition tool is not used. The value (~0x0) means the maximum size remaining.
#define BUILD_SPIFFS #define BUILD_SPIFFS
#define SPIFFS_CACHE 1 // Enable if you use you SPIFFS in R/W mode #define SPIFFS_CACHE 1 // Enable if you use you SPIFFS in R/W mode
//#define SPIFFS_MAX_FILESYSTEM_SIZE 0x20000
#define SPIFFS_MAX_OPEN_FILES 4 // maximum number of open files for SPIFFS #define SPIFFS_MAX_OPEN_FILES 4 // maximum number of open files for SPIFFS
#define FS_OBJ_NAME_LEN 31 // maximum length of a filename #define FS_OBJ_NAME_LEN 31 // maximum length of a filename
@ -211,9 +217,14 @@
#define NODEMCU_SPIFFS0_PARTITION 6 #define NODEMCU_SPIFFS0_PARTITION 6
#define NODEMCU_SPIFFS1_PARTITION 7 #define NODEMCU_SPIFFS1_PARTITION 7
#define LUA_FLASH_STORE 0x0 #ifndef LUA_FLASH_STORE
# define LUA_FLASH_STORE 0x0
#endif
#define SPIFFS_FIXED_LOCATION 0x0 #define SPIFFS_FIXED_LOCATION 0x0
#define SPIFFS_MAX_FILESYSTEM_SIZE (~0x0) #ifndef SPIFFS_MAX_FILESYSTEM_SIZE
# define SPIFFS_MAX_FILESYSTEM_SIZE 0xFFFFFFFF
#endif
//#define SPIFFS_SIZE_1M_BOUNDARY //#define SPIFFS_SIZE_1M_BOUNDARY
#define LUA_TASK_PRIO USER_TASK_PRIO_0 #define LUA_TASK_PRIO USER_TASK_PRIO_0

View File

@ -7,7 +7,7 @@
// includes general purpose interface modules which require at most two GPIO pins. // includes general purpose interface modules which require at most two GPIO pins.
// See https://github.com/nodemcu/nodemcu-firmware/pull/1127 for discussions. // See https://github.com/nodemcu/nodemcu-firmware/pull/1127 for discussions.
// New modules should be disabled by default and added in alphabetical order. // New modules should be disabled by default and added in alphabetical order.
#define LUA_USE_MODULES_ADC //#define LUA_USE_MODULES_ADC
//#define LUA_USE_MODULES_ADS1115 //#define LUA_USE_MODULES_ADS1115
//#define LUA_USE_MODULES_ADXL345 //#define LUA_USE_MODULES_ADXL345
//#define LUA_USE_MODULES_AM2320 //#define LUA_USE_MODULES_AM2320
@ -21,22 +21,22 @@
//#define LUA_USE_MODULES_COLOR_UTILS //#define LUA_USE_MODULES_COLOR_UTILS
//#define LUA_USE_MODULES_CRON //#define LUA_USE_MODULES_CRON
//#define LUA_USE_MODULES_CRYPTO //#define LUA_USE_MODULES_CRYPTO
#define LUA_USE_MODULES_DHT //#define LUA_USE_MODULES_DHT
//#define LUA_USE_MODULES_ENCODER //#define LUA_USE_MODULES_ENCODER
//#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work. //#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work.
#define LUA_USE_MODULES_FILE #define LUA_USE_MODULES_FILE
//#define LUA_USE_MODULES_GDBSTUB #define LUA_USE_MODULES_GDBSTUB
#define LUA_USE_MODULES_GPIO //#define LUA_USE_MODULES_GPIO
//#define LUA_USE_MODULES_GPIO_PULSE //#define LUA_USE_MODULES_GPIO_PULSE
//#define LUA_USE_MODULES_HDC1080 //#define LUA_USE_MODULES_HDC1080
//#define LUA_USE_MODULES_HMC5883L //#define LUA_USE_MODULES_HMC5883L
//#define LUA_USE_MODULES_HTTP //#define LUA_USE_MODULES_HTTP
//#define LUA_USE_MODULES_HX711 //#define LUA_USE_MODULES_HX711
#define LUA_USE_MODULES_I2C //#define LUA_USE_MODULES_I2C
//#define LUA_USE_MODULES_L3G4200D //#define LUA_USE_MODULES_L3G4200D
//#define LUA_USE_MODULES_MCP4725 //#define LUA_USE_MODULES_MCP4725
//#define LUA_USE_MODULES_MDNS //#define LUA_USE_MODULES_MDNS
#define LUA_USE_MODULES_MQTT //#define LUA_USE_MODULES_MQTT
#define LUA_USE_MODULES_NET #define LUA_USE_MODULES_NET
#define LUA_USE_MODULES_NODE #define LUA_USE_MODULES_NODE
#define LUA_USE_MODULES_OW #define LUA_USE_MODULES_OW
@ -54,7 +54,7 @@
//#define LUA_USE_MODULES_SJSON //#define LUA_USE_MODULES_SJSON
//#define LUA_USE_MODULES_SNTP //#define LUA_USE_MODULES_SNTP
//#define LUA_USE_MODULES_SOMFY //#define LUA_USE_MODULES_SOMFY
#define LUA_USE_MODULES_SPI //#define LUA_USE_MODULES_SPI
//#define LUA_USE_MODULES_SQLITE3 //#define LUA_USE_MODULES_SQLITE3
//#define LUA_USE_MODULES_STRUCT //#define LUA_USE_MODULES_STRUCT
//#define LUA_USE_MODULES_SWITEC //#define LUA_USE_MODULES_SWITEC

View File

@ -15,6 +15,7 @@
#define c_memcmp os_memcmp #define c_memcmp os_memcmp
#define c_memcpy os_memcpy #define c_memcpy os_memcpy
#define c_memmove os_memmove
#define c_memset os_memset #define c_memset os_memset
#define c_strcat os_strcat #define c_strcat os_strcat

View File

@ -435,7 +435,7 @@ static int file_g_read( lua_State* L, int n, int16_t end_char, int fd )
int nread = vfs_read(fd, p, nwanted); int nread = vfs_read(fd, p, nwanted);
if (nread == VFS_RES_ERR || nread == 0) { if (nread == VFS_RES_ERR || nread == 0) {
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
} }
@ -495,19 +495,19 @@ static int file_readline( lua_State* L )
static int file_getfile( lua_State* L ) static int file_getfile( lua_State* L )
{ {
// Warning this code C calls other file_* routines to avoid duplication code. These // Warning this code C calls other file_* routines to avoid duplication code. These
// use Lua stack addressing of arguments, so this does Lua stack maniplation to // use Lua stack addressing of arguments, so this does Lua stack maniplation to
// align these // align these
int ret_cnt = 0; int ret_cnt = 0;
lua_settop(L ,1); lua_settop(L ,1);
// Stack [1] = FD // Stack [1] = FD
file_open(L); file_open(L);
// Stack [1] = filename; [2] = FD or nil // Stack [1] = filename; [2] = FD or nil
if (!lua_isnil(L, -1)) { if (!lua_isnil(L, -1)) {
lua_remove(L, 1); // dump filename, so [1] = FD lua_remove(L, 1); // dump filename, so [1] = FD
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj"); file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
ret_cnt = file_g_read(L, LUAI_MAXINT32, EOF, ud->fd); ret_cnt = file_g_read(L, LUAI_MAXINT32, EOF, ud->fd);
// Stack [1] = FD; [2] = contents if ret_cnt = 1; // Stack [1] = FD; [2] = contents if ret_cnt = 1;
file_close(L); // leaves Stack unchanged if [1] = FD file_close(L); // leaves Stack unchanged if [1] = FD
lua_remove(L, 1); // Dump FD leaving contents as [1] / ToS lua_remove(L, 1); // Dump FD leaving contents as [1] / ToS
} }
return ret_cnt; return ret_cnt;
@ -557,23 +557,23 @@ static int file_writeline( lua_State* L )
static int file_putfile( lua_State* L ) static int file_putfile( lua_State* L )
{ {
// Warning this code C calls other file_* routines to avoid duplication code. These // Warning this code C calls other file_* routines to avoid duplication code. These
// use Lua stack addressing of arguments, so this does Lua stack maniplation to // use Lua stack addressing of arguments, so this does Lua stack maniplation to
// align these // align these
int ret_cnt = 0; int ret_cnt = 0;
lua_settop(L, 2); lua_settop(L, 2);
lua_pushvalue(L, 2); //dup contents onto the ToS [3] lua_pushvalue(L, 2); //dup contents onto the ToS [3]
lua_pushliteral(L, "w+"); lua_pushliteral(L, "w+");
lua_replace(L, 2); lua_replace(L, 2);
// Stack [1] = filename; [2] "w+" [3] contents; // Stack [1] = filename; [2] "w+" [3] contents;
file_open(L); file_open(L);
// Stack [1] = filename; [2] "w+" [3] contents; [4] FD or nil // Stack [1] = filename; [2] "w+" [3] contents; [4] FD or nil
if (!lua_isnil(L, -1)) { if (!lua_isnil(L, -1)) {
lua_remove(L, 2); //dump "w+" attribute literal lua_remove(L, 2); //dump "w+" attribute literal
lua_replace(L, 1); lua_replace(L, 1);
// Stack [1] = FD; [2] contents // Stack [1] = FD; [2] contents
file_write(L); file_write(L);
// Stack [1] = FD; [2] contents; [3] result status // Stack [1] = FD; [2] contents; [3] result status
lua_remove(L, 2); //dump contents lua_remove(L, 2); //dump contents
file_close(L); file_close(L);
lua_remove(L, 1); // Dump FD leaving status as ToS lua_remove(L, 1); // Dump FD leaving status as ToS

View File

@ -1,5 +1,4 @@
// Module for interfacing with system // Module for interfacing with system
#include "module.h" #include "module.h"
#include "lauxlib.h" #include "lauxlib.h"
@ -572,6 +571,177 @@ static int node_random (lua_State *L) {
return 1; return 1;
} }
#ifdef DEVELOPMENT_TOOLS
// Lua: rec = node.readrcr(id)
static int node_readrcr (lua_State *L) {
int id = luaL_checkinteger(L, 1);
char *data;
int n = platform_rcr_read(id, (void **)&data);
if (n == ~0) return 0;
lua_pushlstring(L, data, n);
return 1;
}
// Lua: n = node.writercr(id,rec)
static int node_writercr (lua_State *L) {
int id = luaL_checkinteger(L, 1),l;
const char *data = lua_tolstring(L, 2, &l);
int n = platform_rcr_write(id, data, l);
lua_pushinteger(L, n);
return 1;
}
#endif
typedef enum pt_t { lfs_addr=0, lfs_size, spiffs_addr, spiffs_size, max_pt} pt_t;
static const LUA_REG_TYPE pt_map[] = {
{ LSTRKEY( "lfs_addr" ), LNUMVAL( lfs_addr ) },
{ LSTRKEY( "lfs_size" ), LNUMVAL( lfs_size ) },
{ LSTRKEY( "spiffs_addr" ), LNUMVAL( spiffs_addr ) },
{ LSTRKEY( "spiffs_size" ), LNUMVAL( spiffs_size ) },
{ LNILKEY, LNILVAL }
};
// Lua: ptinfo = node.getpartitiontable()
static int node_getpartitiontable (lua_State *L) {
uint32_t param[max_pt] = {0};
param[lfs_size] = platform_flash_get_partition(NODEMCU_LFS0_PARTITION, param + lfs_addr);
param[spiffs_size] = platform_flash_get_partition(NODEMCU_SPIFFS0_PARTITION, param + spiffs_addr);
lua_settop(L, 0);
lua_createtable (L, 0, max_pt); /* at index 1 */
lua_pushrotable(L, (void*)pt_map); /* at index 2 */
lua_pushnil(L); /* first key at index 3 */
while (lua_next(L, 2) != 0) { /* key at index 3, and v at index 4 */
lua_pushvalue(L, 3); /* dup key to index 5 */
lua_pushinteger(L, param[lua_tointeger(L, 4)]); /* param [v] at index 6 */
lua_rawset(L, 1);
lua_pop(L, 1); /* discard v */
}
lua_pop(L, 1); /* discard pt_map reference */
return 1;
}
static void insert_partition(partition_item_t *p, int n, uint32_t type, uint32_t addr) {
if (n>0)
c_memmove(p+1, p, n*sizeof(partition_item_t)); /* overlapped so must be move not cpy */
p->type = type;
p->addr = addr;
p->size = 0;
}
static void delete_partition(partition_item_t *p, int n) {
if (n>0)
c_memmove(p, p+1, n*sizeof(partition_item_t)); /* overlapped so must be move not cpy */
}
#define SKIP (~0)
#define IROM0_PARTITION (SYSTEM_PARTITION_CUSTOMER_BEGIN + NODEMCU_IROM0TEXT_PARTITION)
#define LFS_PARTITION (SYSTEM_PARTITION_CUSTOMER_BEGIN + NODEMCU_LFS0_PARTITION)
#define SPIFFS_PARTITION (SYSTEM_PARTITION_CUSTOMER_BEGIN + NODEMCU_SPIFFS0_PARTITION)
// Lua: node.setpartitiontable(pt_settings)
static int node_setpartitiontable (lua_State *L) {
partition_item_t *rcr_pt = NULL, *pt;
uint32_t flash_size = flash_rom_get_size_byte();
uint32_t i = platform_rcr_read(PLATFORM_RCR_PT, (void **) &rcr_pt);
uint32_t last = 0;
uint32_t n = i / sizeof(partition_item_t);
uint32_t param[max_pt] = {SKIP, SKIP, SKIP, SKIP};
luaL_argcheck(L, lua_istable(L, 1), 1, "must be table");
lua_settop(L, 1);
/* convert input table into 4 option array */
lua_pushrotable(L, (void*)pt_map); /* at index 2 */
lua_pushnil(L); /* first key at index 3 */
while (lua_next(L, 1) != 0) {
/* 'key' (at index 3) and 'value' (at index 4) */
luaL_argcheck(L, lua_isstring(L, 3) && lua_isnumber(L, 4), 1, "invalid partition setting");
lua_pushvalue(L, 3); /* dup key to index 5 */
lua_rawget(L, 2); /* lookup in pt_map */
luaL_argcheck(L, !lua_isnil(L, -1), 1, "invalid partition setting");
param[lua_tointeger(L, 5)] = lua_tointeger(L, 4);
/* removes 'value'; keeps 'key' for next iteration */
lua_pop(L, 2); /* discard value and lookup */
}
/*
* Allocate a scratch Partition Table as userdata on the Lua stack, and copy the
* current Flash PT into this for manipulation
*/
lua_newuserdata(L, (n+2)*sizeof(partition_item_t));
pt = lua_touserdata (L, -1);
c_memcpy(pt, rcr_pt, n*sizeof(partition_item_t));
pt[n].type = 0; pt[n+1].type = 0;
for (i = 0; i < n; i ++) {
partition_item_t *p = pt + i;
if (p->type == IROM0_PARTITION && p[1].type != LFS_PARTITION) {
// if the LFS partition is not following IROM0 then slot a blank one in
insert_partition(p + 1, n-i-1, LFS_PARTITION, p->addr + p->size);
n++;
} else if (p->type == LFS_PARTITION) {
if (p[1].type != SPIFFS_PARTITION) {
// if the SPIFFS partition is not following LFS then slot a blank one in
insert_partition(p + 1, n-i-1, SPIFFS_PARTITION, 0);
n++;
}
// update the LFS options if set
if (param[lfs_addr] != SKIP) {
p->addr = param[lfs_addr];
}
if (param[lfs_size] != SKIP) {
p->size = param[lfs_size];
}
} else if (p->type == SPIFFS_PARTITION) {
// update the SPIFFS options if set
if (param[spiffs_addr] != SKIP) {
p->addr = param[spiffs_addr];
p->size = SKIP;
}
if (param[spiffs_size] != SKIP) {
// BOTCH: - at the moment the firmware doesn't boot if the SPIFFS partition
// is deleted so the minimum SPIFFS size is 64Kb
p->size = param[spiffs_size] > 0x10000 ? param[spiffs_size] : 0x10000;
}
if (p->size == SKIP) {
if (p->addr < 0) {
// This allocate all the remaining flash to SPIFFS
p->addr = last;
p->size = flash_size - last;
} else {
p->size = flash_size - p->addr;
}
} else if (/* size is specified && */ p->addr == 0) {
// if the is addr not specified then start SPIFFS at 1Mb
// boundary if the size will fit otherwise make it consecutive
// to the previous partition.
p->addr = (p->size <= flash_size - 0x100000) ? 0x100000 : last;
}
}
if (p->size == 0) {
// Delete 0-sized partitions as the SDK barfs on these
delete_partition(p, n-i-1);
n--; i--;
} else {
// Do consistency tests on the partition
if (p->addr & (INTERNAL_FLASH_SECTOR_SIZE - 1) ||
p->size & (INTERNAL_FLASH_SECTOR_SIZE - 1) ||
p->addr < last ||
p->addr + p->size > flash_size) {
luaL_error(L, "value out of range");
}
}
}
// for (i = 0; i < n; i ++)
// dbg_printf("Partition %d: %04x %06x %06x\n", i, pt[i].type, pt[i].addr, pt[i].size);
platform_rcr_write(PLATFORM_RCR_PT, pt, n*sizeof(partition_item_t));
while(1); // Trigger WDT; the new PT will be loaded on reboot
return 0;
}
// Module function map // Module function map
@ -605,6 +775,10 @@ static const LUA_REG_TYPE node_map[] =
{ LSTRKEY( "sleep" ), LFUNCVAL( node_sleep ) }, { LSTRKEY( "sleep" ), LFUNCVAL( node_sleep ) },
#ifdef PMSLEEP_ENABLE #ifdef PMSLEEP_ENABLE
PMSLEEP_INT_MAP, PMSLEEP_INT_MAP,
#endif
#ifdef DEVELOPMENT_TOOLS
{ LSTRKEY( "readrcr" ), LFUNCVAL( node_readrcr ) },
{ LSTRKEY( "writercr" ), LFUNCVAL( node_writercr ) },
#endif #endif
{ LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) }, { LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) },
{ LSTRKEY( "flashid" ), LFUNCVAL( node_flashid ) }, { LSTRKEY( "flashid" ), LFUNCVAL( node_flashid ) },
@ -628,6 +802,8 @@ static const LUA_REG_TYPE node_map[] =
#ifdef DEVELOPMENT_TOOLS #ifdef DEVELOPMENT_TOOLS
{ LSTRKEY( "osprint" ), LFUNCVAL( node_osprint ) }, { LSTRKEY( "osprint" ), LFUNCVAL( node_osprint ) },
#endif #endif
{ LSTRKEY( "getpartitiontable" ), LFUNCVAL( node_getpartitiontable ) },
{ LSTRKEY( "setpartitiontable" ), LFUNCVAL( node_setpartitiontable ) },
// Combined to dsleep(us, option) // Combined to dsleep(us, option)
// { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) }, // { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) },

View File

@ -903,10 +903,10 @@ uint32_t platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size )
r = flash_read(fromaddr, to2, size2); r = flash_read(fromaddr, to2, size2);
if(SPI_FLASH_RESULT_OK == r) if(SPI_FLASH_RESULT_OK == r)
{ {
os_memmove(to,to2,size2); c_memmove(to,to2,size2); // This is overlapped so must be memmove and not memcpy
char back[ INTERNAL_FLASH_READ_UNIT_SIZE ] __attribute__ ((aligned(INTERNAL_FLASH_READ_UNIT_SIZE))); char back[ INTERNAL_FLASH_READ_UNIT_SIZE ] __attribute__ ((aligned(INTERNAL_FLASH_READ_UNIT_SIZE)));
r=flash_read(fromaddr+size2,(uint32*)back,INTERNAL_FLASH_READ_UNIT_SIZE); r=flash_read(fromaddr+size2,(uint32*)back,INTERNAL_FLASH_READ_UNIT_SIZE);
os_memcpy((uint8_t*)to+size2,back,INTERNAL_FLASH_READ_UNIT_SIZE); c_memcpy((uint8_t*)to+size2,back,INTERNAL_FLASH_READ_UNIT_SIZE);
} }
} }
else else
@ -936,7 +936,7 @@ static uint32_t flash_map_meg_offset (void) {
return m0 + m1; return m0 + m1;
} }
uint32_t platform_flash_mapped2phys (uint32_t mapped_addr) { uint32_t platform_flash_mapped2phys (uint32_t mapped_addr) {
uint32_t meg = flash_map_meg_offset(); uint32_t meg = flash_map_meg_offset();
return (meg&1) ? -1 : mapped_addr - INTERNAL_FLASH_MAPPED_ADDRESS + meg ; return (meg&1) ? -1 : mapped_addr - INTERNAL_FLASH_MAPPED_ADDRESS + meg ;
} }
@ -946,7 +946,7 @@ uint32_t platform_flash_phys2mapped (uint32_t phys_addr) {
return (meg&1) ? -1 : phys_addr + INTERNAL_FLASH_MAPPED_ADDRESS - meg; return (meg&1) ? -1 : phys_addr + INTERNAL_FLASH_MAPPED_ADDRESS - meg;
} }
uint32_t platform_flash_get_partition (uint32_t part_id, uint32_t *addr) { uint32_t platform_flash_get_partition (uint32_t part_id, uint32_t *addr) {
partition_item_t pt = {0,0,0}; partition_item_t pt = {0,0,0};
system_partition_get_item(SYSTEM_PARTITION_CUSTOMER_BEGIN + part_id, &pt); system_partition_get_item(SYSTEM_PARTITION_CUSTOMER_BEGIN + part_id, &pt);
if (addr) { if (addr) {
@ -954,6 +954,127 @@ uint32_t platform_flash_get_partition (uint32_t part_id, uint32_t *addr) {
} }
return pt.type == 0 ? 0 : pt.size; return pt.type == 0 ? 0 : pt.size;
} }
/*
* The Reboot Config Records are stored in the 4K flash page at offset 0x10000 (in
* the linker section .irom0.ptable) and is used for configuration changes that
* persist across reboots. This page contains a sequence of records, each of which
* is word-aligned and comprises a header and body of length 0-64 words. The 4-byte
* header comprises a length, a RCR id, and two zero fill bytes. These are written
* using flash NAND writing rules, so any unused area (all 0xFF) can be overwritten
* by a new record without needing to erase the RCR page. Ditto any existing
* record can be marked as deleted by over-writing the header with the id set to
* PLATFORM_RCR_DELETED (0x0). Note that the last word is not used additions so a
* scan for PLATFORM_RCR_FREE will always terminate.
*
* The number of updates is extremely low, so it is unlikely (but possible) that
* the page might fill with the churn of new RCRs, so in this case the write function
* compacts the page by eliminating all deleted records. This does require a flash
* sector erase.
*
* NOTE THAT THIS ALGO ISN'T 100% ROBUST, eg. a powerfail between the erase and the
* wite-back will leave the page unitialised; ditto a powerfail between the record
* appned and old deletion will leave two records. However this is better than the
* general integrity of SPIFFS, for example and the vulnerable window is typically
* less than 1 mSec every configuration change.
*/
extern uint32_t _irom0_text_start[];
#define RCR_WORD(i) (_irom0_text_start[i])
#define WORDSIZE sizeof(uint32_t)
#define FLASH_SECTOR_WORDS (INTERNAL_FLASH_SECTOR_SIZE/WORDSIZE)
uint32_t platform_rcr_read (uint8_t rec_id, void **rec) {
//DEBUG os_printf("platform_rcr_read(%d,%08x)\n",rec_id,rec);
platform_rcr_t *rcr = (platform_rcr_t *) &RCR_WORD(0);
uint32_t i = 0;
/*
* Chain down the RCR page looking for a record that matches the record
* ID. If found return the size of the record and optionally its address.
*/
while (1) {
// copy RCR header into RAM to avoid unaligned exceptions
platform_rcr_t r = (platform_rcr_t) RCR_WORD(i);
if (r.id == rec_id) {
if (rec) *rec = &RCR_WORD(i+1);
return r.len * WORDSIZE;
} else if (r.id == PLATFORM_RCR_FREE) {
break;
}
i += 1 + r.len;
}
return ~0;
}
/*
* Chain down the RCR page and look for an existing record that matches the record
* ID and the first free record. If there is enough room, then append the new
* record and mark any previous record as deleted. If the page is full then GC,
* erase the page and rewrite with the GCed content.
*/
#define MAXREC 65
uint32_t platform_rcr_write (uint8_t rec_id, const void *inrec, uint8_t n) {
uint32_t nwords = (n+WORDSIZE-1) / WORDSIZE;
uint32_t reclen = (nwords+1)*WORDSIZE;
uint32_t *prev=NULL, *new = NULL;
// make local stack copy of inrec including header and any trailing fill bytes
uint32_t rec[MAXREC];
if (nwords >= MAXREC)
return ~0;
rec[0] = 0; rec[nwords] = 0;
((platform_rcr_t *) rec)->id = rec_id;
((platform_rcr_t *) rec)->len = nwords;
c_memcpy(rec+1, inrec, n); // let memcpy handle 0 and odd byte cases
// find previous copy if any and exit if the replacement is the same value
uint8_t np = platform_rcr_read (rec_id, (void **) &prev);
if (prev && !os_memcmp(prev-1, rec, reclen))
return n;
// find next free slot
platform_rcr_read (PLATFORM_RCR_FREE, (void **) &new);
uint32_t nfree = &RCR_WORD(FLASH_SECTOR_WORDS) - new;
// Is there enough room to fit the rec in the RCR page?
if (nwords < nfree) { // Note inequality needed to leave at least one all set word
uint32_t addr = platform_flash_mapped2phys((uint32_t)&new[-1]);
platform_s_flash_write(rec, addr, reclen);
if (prev) { // If a previous exists, then overwrite the hdr as DELETED
platform_rcr_t rcr = {0};
addr = platform_flash_mapped2phys((uint32_t)&prev[-1]);
rcr.id = PLATFORM_RCR_DELETED; rcr.len = np/WORDSIZE;
platform_s_flash_write(&rcr, addr, WORDSIZE);
}
} else {
platform_rcr_t *rcr = (platform_rcr_t *) &RCR_WORD(0), newrcr = {0};
uint32_t flash_addr = platform_flash_mapped2phys((uint32_t)&RCR_WORD(0));
uint32_t *buf, i, l, pass;
for (pass = 1; pass <= 2; pass++) {
for (i = 0, l = 0; i < FLASH_SECTOR_WORDS - nfree; ) {
platform_rcr_t r = rcr[i]; // again avoid unaligned exceptions
if (r.id == PLATFORM_RCR_FREE)
break;
if (r.id != PLATFORM_RCR_DELETED && r.id != rec_id) {
if (pass == 2) memcpy(buf + l, rcr + i, (r.len + 1)*WORDSIZE);
l += r.len + 1;
}
i += r.len + 1;
}
if (pass == 2) memcpy(buf + l, rec, reclen);
l += nwords + 1;
if (pass == 1) buf = c_malloc(l * WORDSIZE);
if (l >= FLASH_SECTOR_WORDS || !buf)
return ~0;
}
platform_flash_erase_sector(flash_addr/INTERNAL_FLASH_SECTOR_SIZE);
platform_s_flash_write(buf, flash_addr, l*WORDSIZE);
c_free(buf);
}
return nwords*WORDSIZE;
}
void* platform_print_deprecation_note( const char *msg, const char *time_frame) void* platform_print_deprecation_note( const char *msg, const char *time_frame)
{ {

View File

@ -291,13 +291,6 @@ uint32_t platform_flash_mapped2phys (uint32_t mapped_addr);
uint32_t platform_flash_phys2mapped (uint32_t phys_addr); uint32_t platform_flash_phys2mapped (uint32_t phys_addr);
uint32_t platform_flash_get_partition (uint32_t part_id, uint32_t *addr); uint32_t platform_flash_get_partition (uint32_t part_id, uint32_t *addr);
// *****************************************************************************
// Allocator support
void* platform_get_first_free_ram( unsigned id );
void* platform_get_last_free_ram( unsigned id );
// ***************************************************************************** // *****************************************************************************
// Other glue // Other glue
@ -324,4 +317,40 @@ void* platform_print_deprecation_note( const char *msg, const char *time_frame);
if( !platform_ ## mod ## _check_ ## resmod ## _id( id, resid ) )\ if( !platform_ ## mod ## _check_ ## resmod ## _id( id, resid ) )\
return luaL_error( L, #resmod" %d not valid with " #mod " %d", ( unsigned )resid, ( unsigned )id ) return luaL_error( L, #resmod" %d not valid with " #mod " %d", ( unsigned )resid, ( unsigned )id )
// *****************************************************************************
// Reboot config page
/*
* The 4K flash page in the linker section .irom0.ptable (offset 0x10000) is used
* for configuration changes that persist across reboots. This 4k page contains a
* sequence of records that are written using flash NAND writing rules. See the
* header app/spiffs/spiffs_nucleus.h for a discussion of how SPIFFS uses these. A
* similar technique is used here.
*
* Each record is word aligned and the first two bytes of the record are its size
* and record type. Type 0xFF means unused and type 0x00 means deleted. New
* records can be added by overwriting the first unused slot. Records can be
* replaced by adding the new version, then setting the type of the previous version
* to deleted. This all works and can be implemented with platform_s_flash_write()
* upto the 4K limit.
*
* If a new record cannot fit into the page then the deleted records are GCed by
* copying the active records into a RAM scratch pad, erasing the page and writing
* them back. Yes, this is powerfail unsafe for a few mSec, but this is no worse
* than writing to SPIFFS and won't even occur in normal production use.
*/
#define IROM_PTABLE_ATTR __attribute__((section(".irom0.ptable")))
#define PLATFORM_PARTITION(n) (SYSTEM_PARTITION_CUSTOMER_BEGIN + n)
#define PLATFORM_RCR_DELETED 0x0
#define PLATFORM_RCR_PT 0x1
#define PLATFORM_RCR_PHY_DATA 0x2
#define PLATFORM_RCR_REFLASH 0x3
#define PLATFORM_RCR_FREE 0xFF
typedef union {
uint32_t hdr;
struct { uint8_t len,id; };
} platform_rcr_t;
uint32_t platform_rcr_read (uint8_t rec_id, void **rec);
uint32_t platform_rcr_write (uint8_t rec_id, const void *rec, uint8_t size);
#endif #endif

View File

@ -33,43 +33,66 @@ static task_handle_t input_sig;
static uint8 input_sig_flag = 0; static uint8 input_sig_flag = 0;
/* Contents of esp_init_data_default.bin */ /* Contents of esp_init_data_default.bin */
extern const uint32_t init_data[]; extern const uint32_t init_data[], init_data_end[];
extern const uint32_t init_data_end[]; #define INIT_DATA_SIZE ((init_data_end - init_data)*sizeof(uint32_t))
__asm__( __asm__(
".align 4\n" ".align 4\n"
"init_data: .incbin \"" ESP_INIT_DATA_DEFAULT "\"\n" "init_data: .incbin \"" ESP_INIT_DATA_DEFAULT "\"\n"
"init_data_end:\n" "init_data_end:\n"
); );
extern const char _irom0_text_start[], _irom0_text_end[],_flash_used_end[]; extern const char _irom0_text_start[], _irom0_text_end[], _flash_used_end[];
#define IROM0_SIZE (_irom0_text_end - _irom0_text_start) #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 PRE_INIT_TEXT_ATTR __attribute__((section(".p3.pre_init")))
#define IROM_PTABLE_ATTR __attribute__((section(".irom0.ptable"))) #define IROM_PTABLE_ATTR __attribute__((section(".irom0.ptable")))
#define USED_ATTR __attribute__((used))
#define PARTITION(n) (SYSTEM_PARTITION_CUSTOMER_BEGIN + n) #define PARTITION(n) (SYSTEM_PARTITION_CUSTOMER_BEGIN + n)
#define SIZE_256K 0x00040000 #define SIZE_256K 0x00040000
#define SIZE_1024K 0x00100000 #define SIZE_1024K 0x00100000
#define PT_CHUNK 0x00002000
#define PT_ALIGN(n) ((n + (PT_CHUNK-1)) & (~((PT_CHUNK-1))))
#define FLASH_BASE_ADDR ((char *) 0x40200000) #define FLASH_BASE_ADDR ((char *) 0x40200000)
//TODO: map the TLS server and client certs into NODEMCU_TLSCERT_PARTITION #define NODEMCU_PARTITION_EAGLEROM PLATFORM_PARTITION(NODEMCU_EAGLEROM_PARTITION)
const partition_item_t partition_init_table[] IROM_PTABLE_ATTR = { #define NODEMCU_PARTITION_IROM0TEXT PLATFORM_PARTITION(NODEMCU_IROM0TEXT_PARTITION)
{ PARTITION(NODEMCU_EAGLEROM_PARTITION), 0x00000, 0x0B000}, #define NODEMCU_PARTITION_LFS PLATFORM_PARTITION(NODEMCU_LFS0_PARTITION)
{ SYSTEM_PARTITION_RF_CAL, 0x0B000, 0x1000}, #define NODEMCU_PARTITION_SPIFFS PLATFORM_PARTITION(NODEMCU_SPIFFS0_PARTITION)
{ SYSTEM_PARTITION_PHY_DATA, 0x0C000, 0x1000},
{ SYSTEM_PARTITION_SYSTEM_PARAMETER, 0x0D000, 0x3000}, #define MAX_PARTITIONS 20
{ PARTITION(NODEMCU_IROM0TEXT_PARTITION), 0x10000, 0x0000}, #define WORDSIZE sizeof(uint32_t)
{ PARTITION(NODEMCU_LFS0_PARTITION), 0x0, LUA_FLASH_STORE}, #define PTABLE_SIZE 7 /** THIS MUST BE MATCHED TO NO OF PT ENTRIES BELOW **/
{ PARTITION(NODEMCU_SPIFFS0_PARTITION), 0x0, SPIFFS_MAX_FILESYSTEM_SIZE}, struct defaultpt {
platform_rcr_t hdr;
partition_item_t pt[PTABLE_SIZE+1]; // the +! is for the endmarker
};
#define PT_LEN (NUM_PARTITIONS*sizeof(partition_item_t))
/*
* See app/platform/platform.h for how the platform reboot config records are used
* and these records are allocated. The first record is a default partition table
* and this is statically declared in compilation below.
*/
static const struct defaultpt rompt IROM_PTABLE_ATTR USED_ATTR = {
.hdr = {.len = sizeof(struct defaultpt)/WORDSIZE - 1,
.id = PLATFORM_RCR_PT},
.pt = {
{ NODEMCU_PARTITION_EAGLEROM, 0x00000, 0x0B000},
{ SYSTEM_PARTITION_RF_CAL, 0x0B000, 0x1000},
{ SYSTEM_PARTITION_PHY_DATA, 0x0C000, 0x1000},
{ SYSTEM_PARTITION_SYSTEM_PARAMETER, 0x0D000, 0x3000},
{ NODEMCU_PARTITION_IROM0TEXT, 0x10000, 0x0000},
{ NODEMCU_PARTITION_LFS, 0x0, LUA_FLASH_STORE},
{ NODEMCU_PARTITION_SPIFFS, 0x0, SPIFFS_MAX_FILESYSTEM_SIZE},
{0,(uint32_t) &_irom0_text_end,0} {0,(uint32_t) &_irom0_text_end,0}
}
}; };
// The following enum must maintain the partition table order //TODO: map the TLS server and client certs into NODEMCU_TLSCERT_PARTITION
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) static uint32_t first_time_setup(partition_item_t *pt, uint32_t n, uint32_t flash_size);
#define PT_CHUNK 0x8000 static void phy_data_setup (partition_item_t *pt, uint32_t n);
#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 * 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 * Partition Table.md for further discussion. This version of user_main.c is a
@ -77,118 +100,173 @@ enum partition {iram0=0, rf_call, phy_data, sys_parm, irom0, lfs, spiffs};
* *
* SDK V3 significantly reduces the RAM footprint required by the SDK and introduces * 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 * 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 * this PT for overall allocation of its flash resources. The non_OS SDK calls the
* maintained at the start of IROM0 (flash offset 0x10000) -- see partition_init_table * user_pre_init() entry to do all of this startup configuration. Note that this
* declaration above -- to facilitate its modification either in the firmware binary * runs with Icache enabled -- that is the IROM0 partition is already mapped the
* or in the flash itself. This is Flash PT used during startup to create the live PT * address space at 0x40210000 and so that most SDK services are available, such
* in RAM that is used by the SDK. * as system_get_flash_size_map() which returns the valid flash size (including the
* 8Mb and 16Mb variants).
* *
* Note that user_pre_init() runs with Icache enabled -- that is the IROM0 partition * The first 4K page of IROM0 (flash offset 0x10000) is used to maintain a set of
* is already mapped the address space at 0x40210000 and so that most SDK services * Resource Communication Records (RCR) for inter-boot configuration using a NAND
* are available, such as system_get_flash_size_map() which returns the valid flash * write-once algo (see app/platform/platform.h). One of the current records is the
* size (including the 8Mb and 16Mb variants). * SDK3.0 PT. This build statically compiles in an initial version at the start of
* the PT, with a {0, _irom0_text_end,0} marker as the last record and some fields
* also that need to be recomputed at runtime. This version is either replaced
* by first boot processing after provisioning, or by a node.setpartitiontable()
* API call. These replacement PTs are complete and can be passed directly for use
* by the non-OS SDK.
* *
* We will be separately releasing a host PC-base python tool to configure the PT, * Note that we have released a host PC-base python tool, nodemcu-partition.py, to
* etc., but the following code will initialise the PT to sensible defaults even if * configure the PT, etc during provisioning.
* this tool isn't used.
*/ */
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) { void user_pre_init(void) {
#ifdef LUA_USE_MODULES_RTCTIME #ifdef LUA_USE_MODULES_RTCTIME
// Note: Keep this as close to call_user_start() as possible, since it // Note: Keep this as close to call_user_start() as possible, since it
// is where the cpu clock actually gets bumped to 80MHz. // is where the cpu clock actually gets bumped to 80MHz.
rtctime_early_startup (); rtctime_early_startup ();
#endif #endif
static partition_item_t pt[PTABLE_SIZE]; partition_item_t *rcr_pt = NULL, *pt;
enum flash_size_map fs_size_code = system_get_flash_size_map();
// Flash size lookup is SIZE_256K*2^N where N is as follows (see SDK/user_interface.h)
/* 0 1 2 3 4 5 6 7 8 9 */
/* ½M ¼M 1M 2M 4M 2M 4M 4M 8M 16M */
static char flash_size_scaler[] = "\001\000\002\003\004\003\004\004\005\006";
uint32_t flash_size = SIZE_256K << flash_size_scaler[fs_size_code];
uint32_t pt_size; uint32_t i = platform_rcr_read(PLATFORM_RCR_PT, (void **) &rcr_pt);
uint32_t fs_size_code = setup_partition_table(pt, &pt_size); uint32_t n = i / sizeof(partition_item_t);
if( fs_size_code > 0 && system_partition_table_regist(pt, pt_size, fs_size_code)) {
if (flash_size < SIZE_1024K) {
os_printf("Flash size (%u) too small to support NodeMCU\n", flash_size);
return;
} else {
os_printf("system SPI FI size:%u, Flash size: %u\n", fs_size_code, flash_size );
}
pt = os_malloc(i); // We will work on and register a RAM copy of the PT
// Return if anything is amiss; The SDK will halt if the PT hasn't been registered
if ( !rcr_pt || !pt || n * sizeof(partition_item_t) != i) {
return; return;
} }
os_printf("system_partition_table_regist fail (%u)\n", fs_size_code); os_memcpy(pt, rcr_pt, i);
while(1);
if (pt[n-1].type == 0) {
// If the last PT entry is a {0,XX,0} end marker, then we need first time setup
n = first_time_setup(pt, n-1, flash_size); // return n because setup might shrink the PT
}
if (platform_rcr_read(PLATFORM_RCR_PHY_DATA, NULL)!=0) {
phy_data_setup(pt, n);
}
// Now register the partition and return
// for (i=0;i<n;i++) os_printf("P%d: %3d %06x %06x\n", i, pt[i].type, pt[i].addr, pt[i].size);
if( fs_size_code > 1 && system_partition_table_regist(pt, n, fs_size_code)) {
return;
}
os_printf("Invalid system partition table\n");
while(1); // Trigger WDT}
}
/*
* If the PLATFORM_RCR_PT record doesn't exist then the PHY_DATA partition might
* not have been initialised. This must be set to the proper default init data
* otherwise the SDK will halt on the "rf_cal[0] !=0x05,is 0xFF" error.
*/
static void phy_data_setup (partition_item_t *pt, uint32_t n) {
uint8_t header[sizeof(uint32_t)] = {0};
int i;
for (i = 0; i < n; i++) {
if (pt[i].type == SYSTEM_PARTITION_PHY_DATA) {
uint32_t addr = pt[i].addr;
platform_s_flash_read(header, addr, sizeof(header));
if (header[0] != 0x05) {
uint32_t sector = pt[i].addr/INTERNAL_FLASH_SECTOR_SIZE;
if (platform_flash_erase_sector(sector) == PLATFORM_OK) {
os_printf("Writing Init Data to 0x%08x\n",addr);
platform_s_flash_write(init_data, addr, INIT_DATA_SIZE);
}
}
// flag setup complete so we don't retry this every boot
platform_rcr_write(PLATFORM_RCR_PHY_DATA, &addr, 0);
return;
}
}
// If the PHY_DATA doesn't exist or the write fails then the
// SDK will raise the rf_cal error anyway, so just return.
}
/*
* First time setup does the one-off PT calculations and checks. If these are OK,
* then writes back a new RCR for the updated PT and triggers a reboot. It returns
* on failure.
*/
static uint32_t first_time_setup(partition_item_t *pt, uint32_t n, uint32_t flash_size) {
int i, j, last = 0, newn = n;
/*
* Scan down the PT adjusting and 0 entries to sensible defaults. Also delete any
* zero-sized partitions (as the SDK barfs on these).
*/
for (i = 0, j = 0; i < n; i ++) {
partition_item_t *p = pt + i;
switch (p->type) {
case NODEMCU_PARTITION_IROM0TEXT:
// If the IROM0 partition size is 0 then compute from the IROM0_SIZE. Note
// that the size in the end-marker is used by the nodemcu-partition.py
// script and not here.
if (p->size == 0) {
p->size = PT_ALIGN(IROM0_SIZE);
}
break;
case NODEMCU_PARTITION_LFS:
// Properly align the LFS partition size and make it consecutive to
// the previous partition.
p->size = PT_ALIGN(p->size);
if (p->addr == 0)
p->addr = last;
break;
case NODEMCU_PARTITION_SPIFFS:
if (p->size == ~0x0 && p->addr == 0) {
// This allocate all the remaining flash to SPIFFS
p->addr = last;
p->size = flash_size - last;
} else if (p->size == ~0x0) {
p->size = flash_size - p->addr;
} else if (p->addr == 0) {
// if the is addr not specified then start SPIFFS at 1Mb
// boundary if the size will fit otherwise make it consecutive
// to the previous partition.
p->addr = (p->size <= flash_size - 0x100000) ? 0x100000 : last;
}
}
if (p->size == 0) {
// Delete 0-sized partitions as the SDK barfs on these
newn--;
} else {
// Do consistency tests on the partition
if (p->addr & (INTERNAL_FLASH_SECTOR_SIZE - 1) ||
p->size & (INTERNAL_FLASH_SECTOR_SIZE - 1) ||
p->addr < last ||
p->addr + p->size > flash_size) {
os_printf("Partition %u invalid alignment\n", i);
while(1) {/*system_soft_wdt_feed ();*/}
}
if (j < i) // shift the partition down if we have any deleted slots
pt[j] = *p;
//os_printf("Partition %d: %04x %06x %06x\n", j, p->type, p->addr, p->size);
j++;
last = p->addr + p->size;
}
}
platform_rcr_write(PLATFORM_RCR_PT, pt, newn*sizeof(partition_item_t));
while(1); // Trigger WDT; the new PT will be loaded on reboot
} }
uint32 ICACHE_RAM_ATTR user_iram_memory_is_enabled(void) { uint32 ICACHE_RAM_ATTR user_iram_memory_is_enabled(void) {
@ -277,18 +355,18 @@ void user_init(void)
#endif #endif
system_init_done_cb(nodemcu_init); system_init_done_cb(nodemcu_init);
} }
#if 0
/* /*
* The SDK now establishes exception handlers for EXCCAUSE errors: ILLEGAL, * The SDK now establishes exception handlers for EXCCAUSE errors: ILLEGAL,
* INSTR_ERROR, LOAD_STORE_ERROR, PRIVILEGED, UNALIGNED, LOAD_PROHIBITED, * INSTR_ERROR, LOAD_STORE_ERROR, PRIVILEGED, UNALIGNED, LOAD_PROHIBITED,
* STORE_PROHIBITED. These handlers are established in SDK/app_main.c. * 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() * 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 * which is a fork of our version. The remaining are handled by a static function
* at SDK:app+main.c:offset 0x0348. * at SDK:app+main.c:offset 0x0348. This wrappoer is only needed for debugging.
* */
void __real__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn); 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) { void __wrap__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn) {
os_printf("Exception handler %x %x\n", cause, fn); os_printf("Exception handler %x %x\n", cause, fn);
__real__xtos_set_exception_handler (cause, fn); __real__xtos_set_exception_handler (cause, fn);
} }
*/ #endif

View File

@ -239,6 +239,24 @@ do
end end
``` ```
## node.getpartitiontable()
Get the current LFS and SPIFFS partition information.
#### Syntax
`node.getpartitiontable()`
#### Parameters
none
#### Returns
An array containing entries for `lfs_addr`, `lfs_size`, `spiffs_addr` and `spiffs_size`. The address values are offsets relative to the startof the Flash memory.
#### Example
```lua
print("The LFS size is " .. node.getpartitiontable().lfs_size)
```
## node.heap() ## node.heap()
Returns the current available heap size in bytes. Note that due to fragmentation, actual allocations of this size may not be possible. Returns the current available heap size in bytes. Note that due to fragmentation, actual allocations of this size may not be possible.
@ -407,6 +425,31 @@ target CPU frequency (number)
node.setcpufreq(node.CPU80MHZ) node.setcpufreq(node.CPU80MHZ)
``` ```
## node.setpartitiontable()
Sets the current LFS and / or SPIFFS partition information.
#### Syntax
`node.setpartitiontable(partition_info)`
!!! note
This function is typically only used once during initial provisioning after first flashing the firmware. It does some consistency checks to validate the specified parameters, and it then reboots the ESP module to load the new partition table. If the LFS or SPIFFS regions have changed then you will need to reload LFS, reformat the SPIFSS and reload its contents.
#### Parameters
An array containing one or more of the following enties. The address values are byte offsets relative to the startof the Flash memory. The size values are in bytes. Note that these parameters must be a multiple of 8Kb to align to Flash page boundaries.
- `lfs_addr`. The base address of the LFS region.
- `lfs_size`. The size of the LFS region.
- `spiffs_addr`. The base address of the SPIFFS region.
- `spiffs_size`. The size of the SPIFFS region.
#### Returns
Not applicable. The ESP module will be rebooted for a valid new set, or a Lua error will be thown if inconsistencies are detected.
#### Example
```lua
node.setpartitiontable{lfs_size = 0x20000, spiffs_addr = 0x120000, spiffs_size = 0x20000}
```
## node.sleep() ## node.sleep()

View File

@ -233,7 +233,7 @@ SECTIONS
.irom0.text : ALIGN(0x1000) .irom0.text : ALIGN(0x1000)
{ {
_irom0_text_start = ABSOLUTE(.); _irom0_text_start = ABSOLUTE(.);
*(.irom0.ptable) KEEP(*(.irom0.ptable))
. = ALIGN(0x1000); . = ALIGN(0x1000);
*(.servercert.flash) *(.servercert.flash)
*(.clientcert.flash) *(.clientcert.flash)

View File

@ -6,7 +6,7 @@
# heavily from and including content from esptool.py with full acknowledgement # 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 # under GPL 2.0, with said content: Copyright (C) 2014-2016 Fredrik Ahlberg, Angus
# Gratton, Espressif Systems (Shanghai) PTE LTD, other contributors as noted. # Gratton, Espressif Systems (Shanghai) PTE LTD, other contributors as noted.
# https://github.com/espressif/esptool # https:# github.com/espressif/esptool
# #
# This program is free software; you can redistribute it and/or modify it under # 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 # the terms of the GNU General Public License as published by the Free Software
@ -37,13 +37,14 @@ import copy
import inspect import inspect
import struct import struct
import string import string
import math
__version__ = '1.0' __version__ = '1.0'
__program__ = 'nodemcu-partition.py' __program__ = 'nodemcu-partition.py'
ROM0_Seg = 0x010000 ROM0_Seg = 0x010000
FLASH_PAGESIZE = 0x001000 FLASH_PAGESIZE = 0x001000
FLASH_BASE_ADDR = 0x40200000 FLASH_BASE_ADDR = 0x40200000
PARTITION_TYPES = { PARTITION_TYPE = {
4: 'RF_CAL', 4: 'RF_CAL',
5: 'PHY_DATA', 5: 'PHY_DATA',
6: 'SYSTEM_PARAMETER', 6: 'SYSTEM_PARAMETER',
@ -55,6 +56,10 @@ PARTITION_TYPES = {
106: 'SPIFFS0', 106: 'SPIFFS0',
107: 'SPIFFS1'} 107: 'SPIFFS1'}
IROM0TEXT = 102
LFS = 103
SPIFFS = 106
MAX_PT_SIZE = 20*3 MAX_PT_SIZE = 20*3
FLASH_SIG = 0xfafaa150 FLASH_SIG = 0xfafaa150
FLASH_SIG_MASK = 0xfffffff0 FLASH_SIG_MASK = 0xfffffff0
@ -62,7 +67,14 @@ FLASH_SIG_ABSOLUTE = 0x00000001
WORDSIZE = 4 WORDSIZE = 4
WORDBITS = 32 WORDBITS = 32
PACK_INT = struct.Struct("<I") DEFAULT_FLASH_SIZE = 4*1024*1024
PLATFORM_RCR_DELETED = 0x0
PLATFORM_RCR_PT = 0x1
PLATFORM_RCR_FREE = 0xFF
SPIFFS_USE_ALL = 0xFFFFFFFF
PACK_INT = struct.Struct("<I")
class FatalError(RuntimeError): class FatalError(RuntimeError):
def __init__(self, message): def __init__(self, message):
@ -72,6 +84,37 @@ class FatalError(RuntimeError):
message += " (result was %s)" % hexify(result) message += " (result was %s)" % hexify(result)
return FatalError(message) return FatalError(message)
def alignPT(n):
return 2*FLASH_PAGESIZE*int(math.ceil(n/2/FLASH_PAGESIZE))
def unpack_RCR(data):
RCRword,recs, i = [PACK_INT.unpack_from(data,i)[0] \
for i in range(0, FLASH_PAGESIZE, WORDSIZE)], \
[],0
while RCRword[i] % 256 != PLATFORM_RCR_FREE:
Rlen, Rtype = RCRword[i] % 256, (RCRword[i]/256) % 256
if Rtype != PLATFORM_RCR_DELETED:
rec = [Rtype,[RCRword[j] for j in range(i+1,i+1+Rlen)]]
if Rtype == PLATFORM_RCR_PT:
PTrec = rec[1]
else:
recs.append(rec)
i = i + Rlen + 1
if PTrec is not None:
return PTrec,recs
FatalError("No partition table found")
def repack_RCR(recs):
data = []
for r in recs:
Rtype, Rdata = r
data.append(256*Rtype + len(Rdata))
data.extend(Rdata)
return ''.join([PACK_INT.pack(i) for i in data])
def load_PT(data, args): def load_PT(data, args):
""" """
Load the Flash copy of the Partition Table from the first segment of the IROM0 Load the Flash copy of the Partition Table from the first segment of the IROM0
@ -80,60 +123,107 @@ def load_PT(data, args):
The (possibly) updated PT is then returned with the LFS sizing. 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), PTrec,recs = unpack_RCR(data)
# with the last slot being an end marker (0,size,0) where size is the size of flash_size = fs.args if args.fs is not None else DEFAULT_FLASH_SIZE
# the firmware image.
pt_map = dict() # The partition table format is a set of 3*uint32 fields (type, addr, size),
for i in range(0,MAX_PT_SIZE,3): # with the optional last slot being an end marker (0,size,0) where size is
if pt[i] == 0: # of the firmware image.
n = i // 3
if PTrec[-3] == 0: # Pick out the ROM size and remove the marker
defaultIROM0size = PTrec[-2] - FLASH_BASE_ADDR
del PTrec[-3:]
else:
defaultIROM0size = None
# The SDK objects to zero-length partitions so if the developer sets the
# size of the LFS and/or the SPIFFS partition to 0 then this is removed.
# If it is subsequently set back to non-zero then it needs to be reinserted.
# In reality the sizing algos assume that the LFS follows the IROM0TEXT one
# and SPIFFS is the last partition. We will need to revisit these algos if
# we adopt a more flexible partiton allocation policy. *** BOTCH WARNING ***
for i in range (0, len(PTrec), 3):
if PTrec[i] == IROM0TEXT and args.ls is not None and \
(len(PTrec) == i+3 or PTrec[i+3] != LFS):
PTrec[i+3:i+3] = [LFS, 0, 0]
break break
elif pt[i] in PARTITION_TYPES: if PTrec[-3] != SPIFFS:
pt_map[PARTITION_TYPES[pt[i]]] = i PTrec.extend([SPIFFS, 0, 0])
else:
raise FatalError("Unknown partition type: %u" % pt[i])
flash_used_end = pt[3*n+1] lastEnd, newPT, map = 0,[], dict()
print " Partition Start Size \n ------------------ ------ ------"
if not ('IROM0TEXT' in pt_map and 'LFS0' in pt_map): for i in range (0, len(PTrec), 3):
raise FatalError("Partition table must contain IROM0 and LFS segments") Ptype, Paddr, Psize = PTrec[i:i+3]
i = pt_map['IROM0TEXT'] if Ptype == IROM0TEXT:
if pt[i+2] == 0: # If the IROM0 partition size is 0 then compute from the IROM0_SIZE.
pt[i+2] = (flash_used_end - FLASH_BASE_ADDR) - pt[i+1] # Note that this script uses the size in the end-marker as a default
if Psize == 0:
if defaultIROM0size is None:
raise FatalError("Cannot set the IROM0 partition size")
Psize = alignPT(defaultIROM0size)
j = pt_map['LFS0'] elif Ptype == LFS:
if args.la is not None: # Properly align the LFS partition size and make it consecutive to
pt[j+1] = args.la # the previous partition.
elif pt[j+1] == 0: if args.la is not None:
pt[j+1] = pt[i+1] + pt[i+2] Paddr = args.la
if args.ls is not None:
Psize = args.ls
Psize = alignPT(Psize)
if Paddr == 0:
Paddr = lastEnd
if Psize > 0:
map['LFS'] = {"addr" : Paddr, "size" : Psize}
if args.ls is not None: elif Ptype == SPIFFS:
pt[j+2] = args.ls # The logic here is convolved. Explicit start and length can be
elif pt[j+2] == 0: # set, but the SPIFFS region is aslo contrained by the end of the
pt[j+2] = 0x10000 # previos partition and the end of Flash. The size = -1 value
# means use up remaining flash and the SPIFFS will be moved to the
# 1Mb boundary if the address is default and the specified size
# allows this.
if args.sa is not None:
Paddr = args.sa
if args.ss is not None:
Psize = args.ss if args.ss >= 0 else SPIFFS_USE_ALL
if Psize == SPIFFS_USE_ALL:
# This allocate all the remaining flash to SPIFFS
if Paddr < lastEnd:
Paddr = lastEnd
Psize = flash_size - Paddr
else:
if Paddr == 0:
# if the is addr not specified then start SPIFFS at 1Mb
# boundary if the size will fit otherwise make it consecutive
# to the previous partition.
Paddr = 0x100000 if Psize <= flash_size - 0x100000 else lastEnd
elif Paddr < lastEnd:
Paddr = lastEnd
if Psize > flash_size - Paddr:
Psize = flash_size - Paddr
if Psize > 0:
map['SPIFFS'] = {"addr" : Paddr, "size" : Psize}
k = pt_map['SPIFFS0'] if Psize > 0:
if args.sa is not None: Pname = PARTITION_TYPE[Ptype] if Ptype in PARTITION_TYPE \
pt[k+1] = args.sa else ("Type %d" % Ptype)
elif pt[k+1] == 0: print(" %-18s %06x %06x"% (Pname, Paddr, Psize))
pt[k+1] = pt[j+1] + pt[j+2] # Do consistency tests on the partition
if (Paddr & (FLASH_PAGESIZE - 1)) > 0 or \
(Psize & (FLASH_PAGESIZE - 1)) > 0 or \
Paddr < lastEnd or \
Paddr + Psize > flash_size:
print (lastEnd, flash_size)
raise FatalError("Partition %u invalid alignment\n" % (i/3))
if args.ss is not None: newPT.extend([Ptype, Paddr, Psize])
pt[k+2] = args.ss lastEnd = Paddr + Psize
LFSaddr, LFSsize = pt[j+1], pt[j+2] recs.append([PLATFORM_RCR_PT,newPT])
print ('\nDump of Partition Table\n') return recs, map
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): def relocate_lfs(data, addr, size):
""" """
@ -144,8 +234,7 @@ def relocate_lfs(data, addr, size):
aligned and so first needs scaling by the wordsize.) aligned and so first needs scaling by the wordsize.)
""" """
addr += FLASH_BASE_ADDR addr += FLASH_BASE_ADDR
w = [PACK_INT.unpack_from(data,WORDSIZE*i)[0] \ w = [PACK_INT.unpack_from(data,i)[0] for i in range(0, len(data),WORDSIZE)]
for i in range(0, len(data) // WORDSIZE)]
flash_sig, flash_size = w[0], w[1] flash_sig, flash_size = w[0], w[1]
assert ((flash_sig & FLASH_SIG_MASK) == FLASH_SIG and assert ((flash_sig & FLASH_SIG_MASK) == FLASH_SIG and
@ -155,6 +244,7 @@ def relocate_lfs(data, addr, size):
flash_size //= WORDSIZE flash_size //= WORDSIZE
flags_size = (flash_size + WORDBITS - 1) // WORDBITS flags_size = (flash_size + WORDBITS - 1) // WORDBITS
print WORDSIZE*flash_size, size, len(data), WORDSIZE*(flash_size + flags_size)
assert (WORDSIZE*flash_size <= size and assert (WORDSIZE*flash_size <= size and
len(data) == WORDSIZE*(flash_size + flags_size)) len(data) == WORDSIZE*(flash_size + flags_size))
@ -175,10 +265,10 @@ def main():
def arg_auto_int(x): def arg_auto_int(x):
ux = x.upper() ux = x.upper()
if "MB" in ux: if "M" in ux:
return int(ux[:ux.index("MB")]) * 1024 * 1024 return int(ux[:ux.index("M")]) * 1024 * 1024
elif "KB" in ux: elif "K" in ux:
return int(ux[:ux.index("KB")]) * 1024 return int(ux[:ux.index("K")]) * 1024
else: else:
return int(ux, 0) return int(ux, 0)
@ -187,22 +277,24 @@ def main():
# ---------- process the arguments ---------- # # ---------- process the arguments ---------- #
a = argparse.ArgumentParser( a = argparse.ArgumentParser(
description='%s V%s - ESP8266 NodeMCU Loader Utility' % description='%s V%s - ESP8266 NodeMCU Loader Utility' %
(__program__, __version__), (__program__, __version__),
prog='esplfs') prog=__program__)
a.add_argument('--port', '-p', help='Serial port device') a.add_argument('--port', '-p', help='Serial port device')
a.add_argument('--baud', '-b', type=arg_auto_int, a.add_argument('--baud', '-b', type=arg_auto_int,
help='Serial port baud rate used when flashing/reading') help='Serial port baud rate used when flashing/reading')
a.add_argument('--lfs-addr', '-la', dest="la", type=arg_auto_int, a.add_argument('--flash_size', '-fs', dest="fs", type=arg_auto_int,
help='Flash size used in SPIFFS allocation (Default 4MB)')
a.add_argument('--lfs_addr', '-la', dest="la", type=arg_auto_int,
help='(Overwrite) start address of LFS partition') help='(Overwrite) start address of LFS partition')
a.add_argument('--lfs-size', '-ls', dest="ls", type=arg_auto_int, a.add_argument('--lfs_size', '-ls', dest="ls", type=arg_auto_int,
help='(Overwrite) length of LFS partition') help='(Overwrite) length of LFS partition')
a.add_argument('--lfs-file', '-lf', dest="lf", help='LFS image file') a.add_argument('--lfs_file', '-lf', dest="lf", help='LFS image file')
a.add_argument('--spiffs-addr', '-sa', dest="sa", type=arg_auto_int, a.add_argument('--spiffs_addr', '-sa', dest="sa", type=arg_auto_int,
help='(Overwrite) start address of SPIFFS partition') help='(Overwrite) start address of SPIFFS partition')
a.add_argument('--spiffs-size', '-ss', dest="ss", type=arg_auto_int, a.add_argument('--spiffs_size', '-ss', dest="ss", type=arg_auto_int,
help='(Overwrite) length of SPIFFS partition') help='(Overwrite) length of SPIFFS partition')
a.add_argument('--spiffs-file', '-sf', dest="sf", help='SPIFFS image file') a.add_argument('--spiffs_file', '-sf', dest="sf", help='SPIFFS image file')
arg = a.parse_args() arg = a.parse_args()
@ -228,11 +320,11 @@ def main():
with open(pt_file,"rb") as f: with open(pt_file,"rb") as f:
data = f.read() data = f.read()
pt, pt_map, n = load_PT(data, arg) # ---------- Update the PT if necessary ---------- #
n = n+1
odata = ''.join([PACK_INT.pack(pt[i]) for i in range(0,3*n)]) + \ recs, pt_map = load_PT(data, arg)
"\xFF" * len(data[3*4*n:]) odata = repack_RCR(recs)
odata = odata + "\xFF" * (FLASH_PAGESIZE - len(odata))
# ---------- If the PT has changed then use esptool to rewrite it ---------- # # ---------- If the PT has changed then use esptool to rewrite it ---------- #
@ -246,13 +338,16 @@ def main():
esptool.main(espargs) esptool.main(espargs)
if arg.lf is not None: if arg.lf is not None:
i = pt_map['LFS0'] if 'LFS' not in pt_map:
la,ls = pt[i+1], pt[i+2] raise FatalError("No LFS partition; cannot write LFS image")
la,ls = pt_map['LFS']['addr'], pt_map['LFS']['size']
# ---------- Read and relocate the LFS image ---------- # # ---------- Read and relocate the LFS image ---------- #
with gzip.open(arg.lf) as f: with gzip.open(arg.lf) as f:
lfs = f.read() lfs = f.read()
if len(lfs) > ls:
raise FatalError("LFS partition to small for LFS image")
lfs = relocate_lfs(lfs, la, ls) lfs = relocate_lfs(lfs, la, ls)
# ---------- Write to a temp file and use esptool to write it to flash ---------- # # ---------- Write to a temp file and use esptool to write it to flash ---------- #
@ -264,7 +359,9 @@ def main():
esptool.main(espargs) esptool.main(espargs)
if arg.sf is not None: if arg.sf is not None:
sa = pt[pt_map['SPIFFS0']+1] if 'SPIFFS' not in pt_map:
raise FatalError("No SPIFSS partition; cannot write SPIFFS image")
sa,ss = pt_map['SPIFFS']['addr'], pt_map['SPIFFS']['size']
# ---------- Write to a temp file and use esptool to write it to flash ---------- # # ---------- Write to a temp file and use esptool to write it to flash ---------- #
@ -274,8 +371,8 @@ def main():
# ---------- Clean up temp directory ---------- # # ---------- Clean up temp directory ---------- #
espargs = base + ['--after', 'hard_reset', 'flash_id'] # espargs = base + ['--after', 'hard_reset', 'flash_id']
esptool.main(espargs) # esptool.main(espargs)
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)