Unverified Commit 27a83feb authored by Terry Ellison's avatar Terry Ellison Committed by GitHub
Browse files

Merge pull request #2301 from TerryE/dev-LFS

LFS evaluation version -- second release
parents 2cc195d5 a3c2f48f
File mode changed from 100755 to 100644
...@@ -50,7 +50,7 @@ INCLUDES += -I ../pcm ...@@ -50,7 +50,7 @@ INCLUDES += -I ../pcm
INCLUDES += -I ../platform INCLUDES += -I ../platform
INCLUDES += -I ../spiffs INCLUDES += -I ../spiffs
INCLUDES += -I ../smart INCLUDES += -I ../smart
INCLUDES += -I ../dhtlib INCLUDES += -I ../dht
INCLUDES += -I ../fatfs INCLUDES += -I ../fatfs
INCLUDES += -I ../http INCLUDES += -I ../http
INCLUDES += -I ../sjson INCLUDES += -I ../sjson
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
* This module, when enabled with the LUA_USE_MODULES_GDBSTUB define causes * This module, when enabled with the LUA_USE_MODULES_GDBSTUB define causes
* the gdbstub code to be included and enabled to handle all fatal exceptions. * the gdbstub code to be included and enabled to handle all fatal exceptions.
* This allows you to use the lx106 gdb to catch the exception and then poke * This allows you to use the lx106 gdb to catch the exception and then poke
* around. You can't continue from an exception (at least not easily). * around. You can continue from a break, but attempting to continue from an
* exception usually fails.
* *
* This should not be included in production builds as any exception will * This should not be included in production builds as any exception will
* put the nodemcu into a state where it is waiting for serial input and it has * put the nodemcu into a state where it is waiting for serial input and it has
...@@ -41,8 +42,8 @@ static int lgdbstub_open(lua_State *L) { ...@@ -41,8 +42,8 @@ static int lgdbstub_open(lua_State *L) {
static const LUA_REG_TYPE gdbstub_map[] = { static const LUA_REG_TYPE gdbstub_map[] = {
{ LSTRKEY( "brk" ), LFUNCVAL( lgdbstub_break ) }, { LSTRKEY( "brk" ), LFUNCVAL( lgdbstub_break ) },
{ LSTRKEY( "gdboutput" ), LFUNCVAL( lgdbstub_gdboutput) }, { LSTRKEY( "gdboutput" ), LFUNCVAL( lgdbstub_gdboutput) },
{ LSTRKEY( "open" ), LFUNCVAL( lgdbstub_open) },
{ LNILKEY, LNILVAL } { LNILKEY, LNILVAL }
}; };
NODEMCU_MODULE(GDBSTUB, "gdbstub", gdbstub_map, lgdbstub_open); NODEMCU_MODULE(GDBSTUB, "gdbstub", gdbstub_map, NULL);
/*
** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $
** Initialization of libraries for lua.c
** See Copyright Notice in lua.h
*/
#define linit_c
#define LUA_LIB
#define LUAC_CROSS_FILE
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "luaconf.h"
#include "module.h"
BUILTIN_LIB_INIT( BASE, "", luaopen_base);
BUILTIN_LIB_INIT( LOADLIB, LUA_LOADLIBNAME, luaopen_package);
#if defined(LUA_USE_BUILTIN_IO)
BUILTIN_LIB_INIT( IO, LUA_IOLIBNAME, luaopen_io);
#endif
#if defined (LUA_USE_BUILTIN_STRING)
extern const luaR_entry strlib[];
BUILTIN_LIB_INIT( STRING, LUA_STRLIBNAME, luaopen_string);
BUILTIN_LIB( STRING, LUA_STRLIBNAME, strlib);
#endif
#if defined(LUA_USE_BUILTIN_TABLE)
extern const luaR_entry tab_funcs[];
BUILTIN_LIB_INIT( TABLE, LUA_TABLIBNAME, luaopen_table);
BUILTIN_LIB( TABLE, LUA_TABLIBNAME, tab_funcs);
#endif
#if defined(LUA_USE_BUILTIN_DEBUG) || defined(LUA_USE_BUILTIN_DEBUG_MINIMAL)
extern const luaR_entry dblib[];
BUILTIN_LIB_INIT( DBG, LUA_DBLIBNAME, luaopen_debug);
BUILTIN_LIB( DBG, LUA_DBLIBNAME, dblib);
#endif
#if defined(LUA_USE_BUILTIN_OS)
extern const luaR_entry syslib[];
BUILTIN_LIB( OS, LUA_OSLIBNAME, syslib);
#endif
#if defined(LUA_USE_BUILTIN_COROUTINE)
extern const luaR_entry co_funcs[];
BUILTIN_LIB( CO, LUA_COLIBNAME, co_funcs);
#endif
#if defined(LUA_USE_BUILTIN_MATH)
extern const luaR_entry math_map[];
BUILTIN_LIB( MATH, LUA_MATHLIBNAME, math_map);
#endif
#ifdef LUA_CROSS_COMPILER
const luaL_Reg lua_libs[] = {{NULL, NULL}};
const luaR_table lua_rotable[] = {{NULL, NULL}};
#else
extern const luaL_Reg lua_libs[];
#endif
void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib = lua_libs;
for (; lib->name; lib++) {
if (lib->func)
{
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
}
}
...@@ -17,7 +17,9 @@ ...@@ -17,7 +17,9 @@
#include "platform.h" #include "platform.h"
#include "lrodefs.h" #include "lrodefs.h"
#ifdef LUA_FLASH_STORE
#include "lflash.h"
#endif
#include "c_types.h" #include "c_types.h"
#include "c_string.h" #include "c_string.h"
#include "driver/uart.h" #include "driver/uart.h"
...@@ -149,7 +151,6 @@ static int node_chipid( lua_State* L ) ...@@ -149,7 +151,6 @@ static int node_chipid( lua_State* L )
// lua_pushinteger(L, vdd33); // lua_pushinteger(L, vdd33);
// return 1; // return 1;
// } // }
// Lua: flashid() // Lua: flashid()
static int node_flashid( lua_State* L ) static int node_flashid( lua_State* L )
{ {
...@@ -178,26 +179,16 @@ static int node_heap( lua_State* L ) ...@@ -178,26 +179,16 @@ static int node_heap( lua_State* L )
return 1; return 1;
} }
extern lua_Load gLoad; extern int lua_put_line(const char *s, size_t l);
extern bool user_process_input(bool force); extern bool user_process_input(bool force);
// Lua: input("string") // Lua: input("string")
static int node_input( lua_State* L ) static int node_input( lua_State* L ) {
{
size_t l = 0; size_t l = 0;
const char *s = luaL_checklstring(L, 1, &l); const char *s = luaL_checklstring(L, 1, &l);
if (s != NULL && l > 0 && l < LUA_MAXINPUT - 1) if (lua_put_line(s, l)) {
{ NODE_DBG("Result (if any):\n");
lua_Load *load = &gLoad; user_process_input(true);
if (load->line_position == 0) {
c_memcpy(load->line, s, l);
load->line[l + 1] = '\0';
load->line_position = c_strlen(load->line) + 1;
load->done = 1;
NODE_DBG("Get command:\n");
NODE_DBG(load->line); // buggy here
NODE_DBG("\nResult(if any):\n");
user_process_input(true);
}
} }
return 0; return 0;
} }
...@@ -609,6 +600,13 @@ static const LUA_REG_TYPE node_task_map[] = { ...@@ -609,6 +600,13 @@ static const LUA_REG_TYPE node_task_map[] = {
static const LUA_REG_TYPE node_map[] = 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( "restart" ), LFUNCVAL( node_restart ) },
{ LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) }, { LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) },
{ LSTRKEY( "dsleepMax" ), LFUNCVAL( dsleepMax ) }, { LSTRKEY( "dsleepMax" ), LFUNCVAL( dsleepMax ) },
...@@ -616,11 +614,9 @@ static const LUA_REG_TYPE node_map[] = ...@@ -616,11 +614,9 @@ static const LUA_REG_TYPE node_map[] =
#ifdef PMSLEEP_ENABLE #ifdef PMSLEEP_ENABLE
PMSLEEP_INT_MAP, PMSLEEP_INT_MAP,
#endif #endif
{ LSTRKEY( "info" ), LFUNCVAL( node_info ) },
{ LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) }, { LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) },
{ LSTRKEY( "flashid" ), LFUNCVAL( node_flashid ) }, { LSTRKEY( "flashid" ), LFUNCVAL( node_flashid ) },
{ LSTRKEY( "flashsize" ), LFUNCVAL( node_flashsize) }, { LSTRKEY( "flashsize" ), LFUNCVAL( node_flashsize) },
{ LSTRKEY( "heap" ), LFUNCVAL( node_heap ) },
{ LSTRKEY( "input" ), LFUNCVAL( node_input ) }, { LSTRKEY( "input" ), LFUNCVAL( node_input ) },
{ LSTRKEY( "output" ), LFUNCVAL( node_output ) }, { LSTRKEY( "output" ), LFUNCVAL( node_output ) },
// Moved to adc module, use adc.readvdd33() // Moved to adc module, use adc.readvdd33()
...@@ -637,7 +633,6 @@ static const LUA_REG_TYPE node_map[] = ...@@ -637,7 +633,6 @@ static const LUA_REG_TYPE node_map[] =
{ LSTRKEY( "stripdebug" ), LFUNCVAL( node_stripdebug ) }, { LSTRKEY( "stripdebug" ), LFUNCVAL( node_stripdebug ) },
#endif #endif
{ LSTRKEY( "egc" ), LROVAL( node_egc_map ) }, { LSTRKEY( "egc" ), LROVAL( node_egc_map ) },
{ LSTRKEY( "task" ), LROVAL( node_task_map ) },
#ifdef DEVELOPMENT_TOOLS #ifdef DEVELOPMENT_TOOLS
{ LSTRKEY( "osprint" ), LFUNCVAL( node_osprint ) }, { LSTRKEY( "osprint" ), LFUNCVAL( node_osprint ) },
#endif #endif
......
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
# a generated lib/image xxx.a () # a generated lib/image xxx.a ()
# #
ifndef PDIR ifndef PDIR
GEN_LIBS = mqtt.a GEN_LIBS = libmqtt.a
endif endif
STD_CFLAGS=-std=gnu11 -Wimplicit STD_CFLAGS=-std=gnu11 -Wimplicit
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
# a generated lib/image xxx.a () # a generated lib/image xxx.a ()
# #
ifndef PDIR ifndef PDIR
GEN_LIBS = pcm.a GEN_LIBS = libpcm.a
endif endif
STD_CFLAGS=-std=gnu11 -Wimplicit STD_CFLAGS=-std=gnu11 -Wimplicit
......
...@@ -24,7 +24,7 @@ extern char _flash_used_end[]; ...@@ -24,7 +24,7 @@ extern char _flash_used_end[];
// Helper function: find the flash sector in which an address resides // Helper function: find the flash sector in which an address resides
// Return the sector number, as well as the start and end address of the sector // Return the sector number, as well as the start and end address of the sector
static uint32_t flashh_find_sector( uint32_t address, uint32_t *pstart, uint32_t *pend ) static uint32_t flash_find_sector( uint32_t address, uint32_t *pstart, uint32_t *pend )
{ {
#ifdef INTERNAL_FLASH_SECTOR_SIZE #ifdef INTERNAL_FLASH_SECTOR_SIZE
// All the sectors in the flash have the same size, so just align the address // All the sectors in the flash have the same size, so just align the address
...@@ -53,7 +53,7 @@ static uint32_t flashh_find_sector( uint32_t address, uint32_t *pstart, uint32_t ...@@ -53,7 +53,7 @@ static uint32_t flashh_find_sector( uint32_t address, uint32_t *pstart, uint32_t
uint32_t platform_flash_get_sector_of_address( uint32_t addr ) uint32_t platform_flash_get_sector_of_address( uint32_t addr )
{ {
return flashh_find_sector( addr, NULL, NULL ); return flash_find_sector( addr, NULL, NULL );
} }
uint32_t platform_flash_get_num_sectors(void) uint32_t platform_flash_get_num_sectors(void)
...@@ -73,12 +73,12 @@ uint32_t platform_flash_get_first_free_block_address( uint32_t *psect ) ...@@ -73,12 +73,12 @@ uint32_t platform_flash_get_first_free_block_address( uint32_t *psect )
uint32_t start, end, sect; uint32_t start, end, sect;
NODE_DBG("_flash_used_end:%08x\n", (uint32_t)_flash_used_end); NODE_DBG("_flash_used_end:%08x\n", (uint32_t)_flash_used_end);
if(_flash_used_end>0){ // find the used sector if(_flash_used_end>0){ // find the used sector
sect = flashh_find_sector( platform_flash_mapped2phys ( (uint32_t)_flash_used_end - 1), NULL, &end ); sect = flash_find_sector( platform_flash_mapped2phys ( (uint32_t)_flash_used_end - 1), NULL, &end );
if( psect ) if( psect )
*psect = sect + 1; *psect = sect + 1;
return end + 1; return end + 1;
}else{ } else {
sect = flashh_find_sector( 0, &start, NULL ); // find the first free sector sect = flash_find_sector( 0, &start, NULL ); // find the first free sector
if( psect ) if( psect )
*psect = sect; *psect = sect;
return start; return start;
......
...@@ -116,10 +116,11 @@ bool flash_rom_set_size_type(uint8_t size) ...@@ -116,10 +116,11 @@ bool flash_rom_set_size_type(uint8_t size)
uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR; 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)) if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE))
{ {
((SPIFlashInfo *)(&data[0]))->size = 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)) if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(0 * SPI_FLASH_SEC_SIZE))
{ {
NODE_DBG("\nERASE SUCCESS\n"); NODE_DBG("\nSECTOR 0 ERASE SUCCESS\n");
} }
if (SPI_FLASH_RESULT_OK == spi_flash_write(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) if (SPI_FLASH_RESULT_OK == spi_flash_write(0, (uint32 *)data, SPI_FLASH_SEC_SIZE))
{ {
...@@ -266,6 +267,7 @@ bool flash_rom_set_speed(uint32_t speed) ...@@ -266,6 +267,7 @@ bool flash_rom_set_speed(uint32_t speed)
if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE))
{ {
((SPIFlashInfo *)(&data[0]))->speed = speed_type; ((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)) if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(0 * SPI_FLASH_SEC_SIZE))
{ {
NODE_DBG("\nERASE SUCCESS\n"); NODE_DBG("\nERASE SUCCESS\n");
......
...@@ -879,7 +879,7 @@ uint32_t platform_s_flash_write( const void *from, uint32_t toaddr, uint32_t siz ...@@ -879,7 +879,7 @@ uint32_t platform_s_flash_write( const void *from, uint32_t toaddr, uint32_t siz
if(SPI_FLASH_RESULT_OK == r) if(SPI_FLASH_RESULT_OK == r)
return size; return size;
else{ else{
NODE_ERR( "ERROR in flash_write: r=%d at %08X\n", ( int )r, ( unsigned )toaddr); NODE_ERR( "ERROR in flash_write: r=%d at %p\n", r, toaddr);
return 0; return 0;
} }
} }
...@@ -917,26 +917,35 @@ uint32_t platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size ) ...@@ -917,26 +917,35 @@ uint32_t platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size )
if(SPI_FLASH_RESULT_OK == r) if(SPI_FLASH_RESULT_OK == r)
return size; return size;
else{ else{
NODE_ERR( "ERROR in flash_read: r=%d at %08X\n", ( int )r, ( unsigned )fromaddr); NODE_ERR( "ERROR in flash_read: r=%d at %p\n", r, fromaddr);
return 0; return 0;
} }
} }
int platform_flash_erase_sector( uint32_t sector_id ) int platform_flash_erase_sector( uint32_t sector_id )
{ {
NODE_DBG( "flash_erase_sector(%u)\n", sector_id);
system_soft_wdt_feed (); system_soft_wdt_feed ();
return flash_erase( sector_id ) == SPI_FLASH_RESULT_OK ? PLATFORM_OK : PLATFORM_ERR; return flash_erase( sector_id ) == SPI_FLASH_RESULT_OK ? PLATFORM_OK : PLATFORM_ERR;
} }
uint32_t platform_flash_mapped2phys (uint32_t mapped_addr) static uint32_t flash_map_meg_offset (void) {
{
uint32_t cache_ctrl = READ_PERI_REG(CACHE_FLASH_CTRL_REG); uint32_t cache_ctrl = READ_PERI_REG(CACHE_FLASH_CTRL_REG);
if (!(cache_ctrl & CACHE_FLASH_ACTIVE)) if (!(cache_ctrl & CACHE_FLASH_ACTIVE))
return -1; return -1;
bool b0 = (cache_ctrl & CACHE_FLASH_MAPPED0) ? 1 : 0; uint32_t m0 = (cache_ctrl & CACHE_FLASH_MAPPED0) ? 0x100000 : 0;
bool b1 = (cache_ctrl & CACHE_FLASH_MAPPED1) ? 1 : 0; uint32_t m1 = (cache_ctrl & CACHE_FLASH_MAPPED1) ? 0x200000 : 0;
uint32_t meg = (b1 << 1) | b0; return m0 + m1;
return mapped_addr - INTERNAL_FLASH_MAPPED_ADDRESS + meg * 0x100000; }
uint32_t platform_flash_mapped2phys (uint32_t mapped_addr) {
uint32_t meg = flash_map_meg_offset();
return (meg&1) ? -1 : mapped_addr - INTERNAL_FLASH_MAPPED_ADDRESS + meg ;
}
uint32_t platform_flash_phys2mapped (uint32_t phys_addr) {
uint32_t meg = flash_map_meg_offset();
return (meg&1) ? -1 : phys_addr + INTERNAL_FLASH_MAPPED_ADDRESS - meg;
} }
void* platform_print_deprecation_note( const char *msg, const char *time_frame) void* platform_print_deprecation_note( const char *msg, const char *time_frame)
......
...@@ -279,13 +279,14 @@ int platform_flash_erase_sector( uint32_t sector_id ); ...@@ -279,13 +279,14 @@ int platform_flash_erase_sector( uint32_t sector_id );
/** /**
* Translated a mapped address to a physical flash address, based on the * Translated a mapped address to a physical flash address, based on the
* current flash cache mapping. * current flash cache mapping, and v.v.
* @param mapped_addr Address to translate (>= INTERNAL_FLASH_MAPPED_ADDRESS) * @param mapped_addr Address to translate (>= INTERNAL_FLASH_MAPPED_ADDRESS)
* @return the corresponding physical flash address, or -1 if flash cache is * @return the corresponding physical flash address, or -1 if flash cache is
* not currently active. * not currently active.
* @see Cache_Read_Enable. * @see Cache_Read_Enable.
*/ */
uint32_t platform_flash_mapped2phys (uint32_t mapped_addr); uint32_t platform_flash_mapped2phys (uint32_t mapped_addr);
uint32_t platform_flash_phys2mapped (uint32_t phys_addr);
// ***************************************************************************** // *****************************************************************************
// Allocator support // Allocator support
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
# a generated lib/image xxx.a () # a generated lib/image xxx.a ()
# #
ifndef PDIR ifndef PDIR
GEN_LIBS = tsl2561lib.a GEN_LIBS = libtsl2561.a
endif endif
STD_CFLAGS=-std=gnu11 -Wimplicit STD_CFLAGS=-std=gnu11 -Wimplicit
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
# a generated lib/image xxx.a () # a generated lib/image xxx.a ()
# #
ifndef PDIR ifndef PDIR
GEN_LIBS = u8glib.a GEN_LIBS = libu8glib.a
endif endif
STD_CFLAGS=-std=gnu11 -Wimplicit STD_CFLAGS=-std=gnu11 -Wimplicit
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
# a generated lib/image xxx.a () # a generated lib/image xxx.a ()
# #
ifndef PDIR ifndef PDIR
GEN_LIBS = ucglib.a GEN_LIBS = libucglib.a
endif endif
STD_CFLAGS=-std=gnu11 -Wimplicit STD_CFLAGS=-std=gnu11 -Wimplicit
......
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
static exception_handler_fn load_store_handler; static exception_handler_fn load_store_handler;
void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause)
{ {
/* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */ /* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */
...@@ -73,13 +72,15 @@ void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) ...@@ -73,13 +72,15 @@ void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause)
{ {
die: die:
/* Turns out we couldn't fix this, so try and chain to the handler /* Turns out we couldn't fix this, so try and chain to the handler
* that was set by the SDK. If none then trigger a system break instead * that was set. (This is typically a remote GDB break). If none
* and hang if the break doesn't get handled. This is effectively * then trigger a system break instead and hang if the break doesn't
* what would happen if the default handler was installed. */ * get handled. This is effectively what would happen if the default
* handler was installed. */
if (load_store_handler) { if (load_store_handler) {
load_store_handler(ef, cause); load_store_handler(ef, cause);
return;
} }
asm ("break 1, 1"); asm ("break 1, 1");
while (1) {} while (1) {}
} }
......
...@@ -129,8 +129,7 @@ bool user_process_input(bool force) { ...@@ -129,8 +129,7 @@ bool user_process_input(bool force) {
return task_post_low(input_sig, force); return task_post_low(input_sig, force);
} }
void nodemcu_init(void) void nodemcu_init(void) {
{
NODE_ERR("\n"); NODE_ERR("\n");
// Initialize platform first for lua modules. // Initialize platform first for lua modules.
if( platform_init() != PLATFORM_OK ) if( platform_init() != PLATFORM_OK )
...@@ -139,11 +138,13 @@ void nodemcu_init(void) ...@@ -139,11 +138,13 @@ void nodemcu_init(void)
NODE_DBG("Can not init platform for modules.\n"); NODE_DBG("Can not init platform for modules.\n");
return; return;
} }
uint32_t size_detected = flash_detect_size_byte();
if( flash_detect_size_byte() != flash_rom_get_size_byte() ) { uint32_t size_from_rom = flash_rom_get_size_byte();
NODE_ERR("Self adjust flash size.\n"); 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. // Fit hardware real flash size.
flash_rom_set_size_byte(flash_detect_size_byte()); flash_rom_set_size_byte(size_detected);
system_restart (); system_restart ();
// Don't post the start_lua task, we're about to reboot... // Don't post the start_lua task, we're about to reboot...
...@@ -260,6 +261,5 @@ void user_init(void) ...@@ -260,6 +261,5 @@ void user_init(void)
#ifndef NODE_DEBUG #ifndef NODE_DEBUG
system_set_os_print(0); system_set_os_print(0);
#endif #endif
system_init_done_cb(nodemcu_init); system_init_done_cb(nodemcu_init);
} }
## Lua Compact Debug (LCD)
LCD (Lua Compact Debug) was developed in Sept 2015 by Terry Ellison as a patch to the Lua system to decrease the RAM usage of Lua scripts. This makes it possible to run larger Lua scripts on systems with limited RAM. Its use is most typically for eLua-type applications, and in this version it targets the **NodeMCU** implementation for the ESP8266 chipsets.
This section gives a full description of LCD. If you are writing **NodeMCU** Lua modules, then this paper will be of interest to you, as it shows how to use LCD in an easy to configure way. *Note that the default `user_config.h` has enabled LCD at a level 2 stripdebug since mid-2016*.
### Motivation
The main issue that led me to write this patch is the relatively high Lua memory consumption of its embedded debug information, as this typically results in a 60% memory increase for most Lua code. This information is generated when any Lua source is complied because the Lua parser uses this as meta information during the compilation process. It is then retained by default for use in generating debug information. The only standard method of removing this information is to use the “strip” option when precompiling source using a standard eLua **luac.cross** on the host, or (in the case of NodeMCU) using the `node.compile()` function on the target environment.
Most application developers that are new to embedded development simply live with this overhead, because either they aren't familiar with these advanced techniques, or they want to keep the source line information in error messages for debugging.
The standard Lua compiler generates fixed 4 byte instructions which are interpreted by the Lua VM during execution. The debug information consists of a map from instruction count to source line number (4 bytes per instruction) and two tables keyed by the names of each local and upvalue. These tables contain metadata on these variables used in the function. This information can be accessed to enable symbolic debugging of Lua source (which isn't supported on **NodeMCU** platforms anyway), and the line number information is also used to generate error messages.
This overhead is sufficient large on limited RAM systems to replace this scheme by making two changes which optimize for space rather than time:
- The encoding scheme used in this patch typically uses 1 byte per source line instead of 4 bytes per instruction, and this represents a 10 to 20-fold reduction in the size of this vector. The access time during compile is still **O(1)**, and **O(N)** during runtime error handling, where **N** is number of non-blank lines in the function. In practice this might add a few microseconds to the time take to generate the error message for typical embedded functions.
- The line number, local and upvalue information is needed during the compilation of a given compilation unit (a source file or source string), but its only use after this is for debugging and so can be discarded. (This is what the `luac -s` option and `node.compile()` do). The line number information if available is used in error reporting. An extra API call has therefore been added to discarded this debug information on completion of the compilation.
To minimise the impact within the C source code for the Lua system, an extra system define **LUA_OPTIMIZE_DEBUG** can be set in the `user_config.h` file to configure a given firmware build. This define sets the default value for all compiles and can take one of four values:
1. (or not defined) use the default Lua scheme.
2. Use compact line encoding scheme and retain all debug information.
3. Use compact line encoding scheme and only retain line number debug information.
4. Discard all debug information on completion of compile.
Building the firmware with the 0 option compiles to the pre-patch version. Options 1-3 generate the `strip_debug()` function, which allows this default value to be set at runtime.
_Note that options 2 and 3 can also change the default behaviour of the `loadstring()` function in that any functions declared within the string cannot inherited any outer locals within the parent hierarchy as upvalues if these have been stripped of the locals and upvalues information._
### Details
There are various API calls which compile and load Lua source code. During compilation each variable name is parsed, and is then resolved in the following order:
- Against the list of local variables declared so far in the current scope is scanned for a match.
- Against the local variable lists for each of the lexically parent functions are then scanned for a match, and if found the variable is tagged as an _upvalue_.
- If unmatched against either of these local scopes then the variable defaults to being a global reference.
The parser and code generator must therefore access the line mapping, upvalues, and locals information tables maintained in each function Prototype header during source compilation. This scoping scheme works because function compilation is recursive: if function A contains the definition of function B which contains the definition of function C, then the compilation of A is paused to compile B and this is in turn paused to compile C; then B complete and then A completes.
The variable meta information is stored in standard Lua tables which are allocated using the standard Lua doubling algorithm and hence they can contain a lot of unused space. The parser therefore calls `close_func()` once compilation of a function has been completed to trim these vectors to the final sizes.
The patch makes the following if `LUA_OPTIMIZE_DEBUG` > 0. (The existing functionality is preserved if this define is zero or undefined.)
- It adds an extra API call: `stripdebug([level[, function]])` as discussed below.
- It extends the trim logic in `close_func()` to replace this trim action by deleting the information according to the current default debug optimization level.
- The `lineinfo` vector associated with each function is replaced by a `packedlineinfo` string using a run length encoding scheme that uses a repeat of an optional line number delta (this is omitted if the line offset is zero) and a count of the number of instruction generated for that source line. This scheme uses roughly an **M** byte vector where **M** is the number of non-blank source lines, as opposed to a **4N** byte vector where **N** is the number of VM instruction. This vector is built sequentially during code generation so it is this patch conditionally replaces the current map with an algorithm to generate the packed version on the fly.
The `stripdebug([level[, function]])` call is processed as follows:
- If both arguments are omitted then the function returns the current default strip level.
- If the function parameter is omitted, then the level is used as the default setting for future compiles. The level must be 1-3 corresponding to the above debug optimization settings. Hence if `stripdebug(3)` is included in **init.lua**, then all debug information will be stripped out of subsequently compiled functions.
- The function parameter if present is parsed in the same way as the function argument in `setfenv()` (except that the integer 0 level is not permitted, and this function tree corresponding to this scope is walked to implement this debug optimization level.
The `packedlineinfo` encoding scheme is as follows:
- It comprises a repeat of (optional) line delta + VM instruction count (IC) for that line starting from a base line number of zero. The line deltas are optional because line deltas of +1 are assumed as default and therefore not emitted.
- ICs are stored as a single byte with the high bit set to zero. Sequences longer than 126 instructions for a single sequence are rare, but can be are encoded using a multi byte sequence using 0 line deltas, e.g. 126 (0) 24 for a line generating 150 VM instructions. The high bit is always unset, and note that this scheme reserves the code 0x7F as discussed below.
- Line deltas are stored with the high bit set and are variable (little-endian) in length. Since deltas are always delimited by an IC which has the top bit unset, the length of each delta can be determined from these delimiters. Deltas are stored as signed ones-compliment with the sign bit in the second bit of low order byte, that is in the format (in binary) `1snnnnnnn [1nnnnnnn]*`, with `s` denoting the sign and `n…n` the value element using the following map. This means that a single byte is used encode line deltas in the range -63 … 65; two bytes used to encode line deltas in the range -8191 … 8193, etc..
```C
value = (sign == 1) ? -delta : delta - 2
```
- This approach has no arbitrary limits, in that it can accommodate any line delta or IC count. Though in practice, most deltas are omitted and multi-byte sequences are rarely generated.
- The codes 0x00 and 0x7F are reserved in this scheme. This is because Lua allocates such growing vectors on a size-doubling basis. The line info vector is always null terminated so that the standard **strlen()** function can be used to determine its length. Any unused bytes between the last IC and the terminating null are filled with 0x7F.
The current mapping scheme has **O(1)** access, but with a code-space overhead of some 140%. This alternative approach has been designed to be space optimized rather than time optimized. It requires the actual IC to line number map to be computed by linearly enumerating the string from the low instruction end during execution, resulting in an **O(N)** access cost, where **N** is the number of bytes in the encoded vector. However, code generation builds this information incrementally, and so only appends to it (or occasionally updates the last element's line number), and the patch adds a couple of fields to the parser `FuncState` record to enable efficient **O(1)** access during compilation.
### Testing
Essentially testing any eLua compiler or runtime changes are a total pain, because eLua is designed to be build against a **newlib**-based ELF. Newlib uses a stripped down set of headers and libraries that are intended for embedded use (rather than being ran over a standard operating system). Gdb support is effectively non-existent, so I found it just easier first to develop this code on a standard Lua build running under Linux (and therefore with full gdb support), and then port the patch to NodeMCU once tested and working.
I tested my patch in standard Lua built with "make generic" and against the [Lua 5.1 suite](http://lua-users.org/lists/lua-l/2006-03/msg00723.html). The test suite was an excellent testing tool, and it revealed a number of cases that exposed logic flaws in my approach, resulting from Lua's approach of not carrying out inline status testing by instead implementing a throw / catch strategy. In fact I realised that I had to redesign the vector generation algorithm to handle this robustly.
As with all eLua builds the patch assumes Lua will not be executing in a multithreaded environment with OS threads running different lua_States. (This is also the case for the NodeMCU firmware). It executes the full test suite cleanly as maximum test levels and I also added some specific tests to cover new **stripdebug** usecases.
Once this testing was completed, I then ported the patch to the NodeMCU build. This was pretty straight forward as this code is essentially independent of the NodeMCU functional changes. The only real issue as to ensure that the NodeMCU `c_strlen()` calls replaced the standard `strlen()`, etc.
I then built both `luac.cross` and firmware images with the patch disable to ensure binary compatibility with the non-patched version and then with the patch enabled at optimization level 3.
In use there is little noticeable difference other than the code size during development are pretty much the same as when running with `node.compile()` stripped code. The new option 2 (retaining packed line info only) has such a minimal size impact that its worth using this all the time. I've also added a separate patch to NodeMCU (which this assumes) so that errors now generate a full traceback.
### How to enable LCD
Enabling LCD is simple: all you need is a patched version and define `LUA_OPTIMIZE_DEBUG` at the default level that you want in `app/include/user_config.h` and do a normal make.
Without this define enabled, the unpatched version is generated.
Note that since `node.compile()` strips all debug information, old **.lc** files generated by this command will still run under the patched firmware, but binary files which retain debug information will not work across patched and non-patched versions.
Other than optionally including a `node.stripdebug(N)` or whatever in your **init.lua**, the patch is otherwise transparent at an application level.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment