Boot ROM and SDK Notes - mhightower83/Arduino-ESP8266-misc GitHub Wiki
WIP - some information has not been verified.
Booting
- Boot ROM Espressif/Xtensa code
main();
eboot.bin
Arduino ESP8266 Boot loaderapp_entry();
in Arduino ESP8266 Core, handle preliminary setup before calling the SDK Start entry point.app_entry_custom;
app_entry_redefinable();
Used to create option to reclaim 4K of Heap/RAM
call_user_start()
NONOS SDK entry point- Somehow the "C" library init code that zeros things gets called.
user_init();
Arduino ESP8266 Core, this is the start of the Primary Arduino ESP8266 Core initialization.- By this time the Boot ROM
flashchip
structure has been updated by the SDK with the boot image header flash size.- If boot image is configured for greater than 4MByte, calls to Boot ROM functions that write/erase flash addresses above 4MByte will fail, without this update.
- More initialization
- Additional calls finish the "C"/"C++" Initialization.
- By this time the Boot ROM
Printing
There are two UARTs: 0 and 1. They are not equal. UART0 has both a TX and RX pin available. UART1 only has the TX pin available. UART1's RX pin was reassigned so that an external FLASH chip could be supported. Device programming happens with UART0. UART0 is also the default serial device when the ESP8266 boots. It always reports boot status messages at startup. UART1 is considered an optional debug output/printer port.
putc1
and putc2
are character print drivers, they are called to print a character, which may result in writing a character to the UART TX register or some other defined destination, like a memory buffer. The ESP8266 has internal/builtin drivers, They have no symbol names so I refer to them as default_putc1 and default_putc2. There is an allowance for external drivers being installed/registered, using ets_install_putc1
or ets_install_putc2
. Now things get confusing. Some printf functions use the builtin putc drivers only, some use the registered drivers only. And in some, not all cases, the builtin driver will be used if one is not registered.
There are many printf like functions int the Boot ROM and SDK most if not all use ets_vprintf()
to complete the task. ets_vprintf()
argument list is not modeled after the POSIX vprintf()
that you may be familiar with. This printf differs by having a function, character print driver
, as the 1st argument. The ESP8266 RTOS SDK maps its Boot ROM function address to ets_io_vprintf()
which I think is a more appropriate name.
It should be noted, that the SDK does not document much in the way of Boot ROM or SDK printf functions.
There are os_printf
, os_printf_plus
, ets_install_putc1
, system_set_os_print
, uart_init
, uart0_tx_buffer
, uart0_rx_intr_handler
, uart_div_modify
, system_uart_swap
, system_uart_de_swap
, ...
Summary of printf functions:
ets_install_uart_printf
- Registers internal default_putc1
(a UART driver) driver for putc1
ets_install_external_printf
- Registers pre and post printf callbacks as well as an optional putc2 driver, otherwise the internal default_putc2
(print to buffer) driver is installed. dangerous
ets_vprintf
- The foundation for all the Boot ROM printf functions.
ets_printf
- Uses ets_write_char
to write to the registered putc1 and/or putc2 drivers. Also uses the registered pre and post printf callbacks.
ets_uart_printf
- Uses internal default_putc1
, ignores registered putc1.
ets_external_printf
- Uses internal default_putc2
, ignores registered putc2. Also calls pre and post printf callbacks.
ets_install_putc1
Installs a character print driver function to print a character.
This function is similar to int putchar(int c)
found in Linux.
To ensure that printing can be handled from an ISR, the driver must be in IRAM or ROM.
Syntax
void ets_install_putc1( void (* pfn)(char c) );
Parameter
Pointer to print driver function.
void pfn(char c);
Return Value
Type: void
Example
void nullPrint(char c) { (void)c; }
...
ets_install_putc1(nullPrint);
Remarks
- Prototype in NONOS SDK,
include/osapi.h
- Boot ROM function at
0x4000242c
- putc1 driver stored at
0x3fffdd48
,0x3fffdd3c + 12
- called by
ets_install_uart_printf
- Boot ROM default putc1 driver function is at
0x40001dcc
. - Proper handling of line ending characters
'\n'
and/or'\r'
must be performed by the putc1 driver.- eg. filter out
'\r'
occurances and print'\n'
as'\r'
,'\n'
- eg. filter out
- No stack frame
See Also
ets_install_putc2
Installs a character print driver function to print a character.
The purpose of this driver is not clear.
It looks like it is intended for non-standard device printing, maybe to a buffer.
In fact, that is what the default_putc2 does.
This function is similar to int putchar(int c)
found in Linux.
To ensure that printing can be handled from an ISR, the driver must be in IRAM or ROM.
Syntax
void ets_install_putc2( void (* pfn)(char c) )
Parameter
Pointer to print driver function.
void pfn(char c);
Return Value
Type: void
Example
struc _SNPRINTF_BUF {
unsigned short length;
unsigned short space;
char *next_char;
char buffer[0];
} *snprintf_buf;
size_t constexpr buffer_size = 128;
...
snprintf_buf = (struc _SNPRINTF_BUF *)malloc(buffer_size + sizeof(struc _SNPRINTF_BUF));
if (snprintf_buf) {
snprintf_buf->next_char = &snprintf_buf->buffer[0];
snprintf_buf->length = snprintf_buf->space = buffer_size;
buffered_print.space -= 1;
}
void my_putc2(char c) {
if (snprintf_buf->space && snprintf_buf->next_char) {
*snprintf_buf->next_char = c;
snprintf_buf->next_char++;
snprintf_buf->space -= 1;
}
}
...
ets_install_putc2(my_putc2);
Remarks
- Not included in SDK header files.
- Boot ROM function at
0x4000248c
- putc2 driver stored at
0x3fffdd4c
,0x3fffdd3c + 16
- called by
ets_install_external_printf
- Boot ROM
default_putc2
driver function address is0x400024a8
. - No stack frame
default_putc1
Default character print driver function to print a character via UART.
Has special handling for \n
and \r
. When called with '\r` it is not printed;
however, when called with '\n' a '\r' and '\n' are printed.
Syntax
void default_putc1(char c)`
Remarks
- Boot ROM
default_putc1
driver function address0x40001dcc
. - pointer to registered putc1 print driver.
*((void **)0x3fffdd48) 3fffdd3c + 12
- Internal Boot ROM function, no LD mappings.
- no stack frame
See Also
ets_install_uart_printf
Installs a UART based character print driver function, default_putc1
. Print characters to UART0 or UART1.
Syntax
void *ets_install_uart_printf(void)
Remarks
- Not included in SDK header files.
- Boot ROM function at
0x40002438
- Default putc1 driver
0x40001dcc
- Calls
ets_install_putc1()
- Happens to return the address of the
default_putc1
driver. - Stack frame
See Also
GetUartDevice
UartDevice structure is used by default_putc1
driver and updated by uart_buff_switch
.
Syntax
UartDevice *GetUartDevice(void);
Return Value
Type: UartDevice * Pointer to UartDevice structure.
typedef struct {
UartBautRate baut_rate; // +0
UartBitsNum4Char data_bits; // +4
UartExistParity exist_parity; // +8
UartParityMode parity; // +12
UartStopBitsNum stop_bits; // +16
UartFlowCtrl flow_ctrl; // +20
typedef struct {
uint32_t RcvBuffSize; // +24
uint8_t *pRcvMsgBuff; // +28
uint8_t *pWritePos; // +32
uint8_t *pReadPos; // +38
uint8_t TrigLvl; // +40 //JLU: may need to pad
RcvMsgBuffState BuffState; // +44
} RcvMsgBuff rcv_buff;
typedef struct {
uint32_t TrxBuffSize; // +48
uint8_t *pTrxBuff; // +52
} TrxMsgBuff trx_buff;
RcvMsgState rcv_state; // +56
int32_t received; // +60
int32_t buff_uart_no; // +64 - indicate which uart use tx/rx buffer
} UartDevice;
Remarks
- Not included in SDK header files; however, structure UartDevice appears in
driver_lib/include/driver/uart.h
andexamples/esp_mqtt_proj/include/driver/uart.h
- Boot ROM function address
0x40003f4c
- Returns UartDevice address
0x3fffde10
- Issue, The data presented may not be true. The hardware can be reconfigured without
UartDev
being updated. eg. baud rate, bits, etc. If one UART is configured differently from the other, then even a call touart_buff_switch
can result in a mismatch of parameters, aside from the one or two that it sets. - Not included in SDK header files.
- no stack frame
See also
ESP8266_NONOS_SDK/driver_lib/include/driver/uart.h
default_putc1
ets_install_putc1
ets_install_uart_printf
uart_buff_switch
ets_putc
A wrapper for uart_tx_one_char
. Prints character ch on the selected UART. At boot, the selected UART defaults to UART0. This selection can be changed through calls to uart_buff_switch
.
Syntax
int ets_putc(char ch);
Return Value
Type: int
Always 0
Remarks
- Not included in SDK header files
- Boot ROM function at
0x40002be8
- If the function is redefined to have prototype
int ets_putc(int ch)
the return value will be the characterch
as an unsigned char upgraded to int. - Return value was that returned by
uart_tx_one_char
- defines a stack frame
uart_buff_switch
Use to select a port for printing: UART0 or UART1
- 0 - Selects UART0, and clears RX FIFO.
- 1 - selects UART1
This API updates entries in UartDev, (0x3fffde10)
, which in turn are referenced by the internal function uart_tx_one_char
.
Syntax
void uart_buff_switch(uint8_t uart_no);
Remarks
- Not included in SDK header files.
- Boot ROM function at
0x400038a4
- There is only one
UartDev
structure so its values represent the current UART selected; however, this API only changes two entries. Most of the entries are not changed and may be stale. putc1 only uses thebuff_uart_no
entry, which this API sets. - Sets
*((uint32_t *)(0x40003224 + 44)) = 0;
only if (uart_no == 0) - Sets
*((uint32_t *)(0x40003224 + 64)) = uart_no;
- Stack frame
See Also
ESP8266_NONOS_SDK/driver_lib/include/driver/uart.h
GetUartDevice
ets_putc
default_putc1
uart_tx_one_char
ets_install_uart_printf
ets_uart_printf
uart_tx_one_char
An internal Boot ROM function that handles printing to one of the two UARTs. uart_tx_one_char
uses data stored at UartDevice.buff_uart_no
to select the UART. UartDevice.buff_uart_no
is set by a previous call to uart_buff_switch
.
This function is called by many higher level functions for printing.
Return value
Type: int
, Always 0
Remarks
- At boot UART0 is selected.
See Also
ets_uart_printf
Uses Boot ROM default putc1 driver.
Use uart_buff_switch
to select UART for printing.
Syntax
int ets_uart_printf(const char *fmt, ...);
Remarks
- Not included in SDK header files.
- Boot ROM function at
0x40002438
- Default putc1 driver
0x40001dcc
- Uses
ets_vprintf()
- Stack frame
system_set_os_print
Debug logging control. When enabled the SDK will print cryptic strings through the installed UART drivers. 1 - enables SDK debug logging 0 - disable printing debug logging
Syntax
void system_set_os_print(uint_t onoff);
Remarks
- SDK function
- onoff is evaluaded as 0 or not 0.
- may not have stack frame
system_get_os_print
Returns a 1 when SDK debug printing is enabled otherwise it returns 0.
Syntax
uint8_t system_get_os_print(void);
Remarks
- SDK function
- No stack frame
os_printf_plus
Debug printf function. Output can be muted or enabled by calls to system_set_print()
.
When fmt
is in flash, os_printf_plus will work best with fmt
at 64 characters or less.
If fmt
is in flash, a check is made to see if there is enough heap space to handle copying from flash to DRAM. If not, fmt
is truncated to 64 characters and copied to a stack buffer.
Syntax
int os_printf_plus(const char *fmt, ...);
Returns
Type: int
Bug - Always returns 176.
Remarks
- SDK IRAM function
- SDK include file
osapi.h
- Supports
fmt
in flash, flash string must be align(4). - Alias
os_printf
- uses 176 bytes of stack space
- Does not appear to have a return value. Always returns 176 which happens to be the stack size.
- May call:
ets_strlen
,ets_write_char
,ets_vprintf
,ets_memcpy
,pvPortMalloc
,vPortFree
,xPortWantedSizeAlign
,system_get_free_heap_size
- May call malloc if
fmt
is in flash. - Only handles simple print formats: %d, %D, %u, %U %c, %C, %s, %S, %x, %X, %p. No support for float, %f. And %S does not support PROGMEM.
- Stack frame.
ets_write_char
A simple function that calls the registered print drive callback functions, putc1, then putc2. If their registry entry is NULL, they are skipped.
Syntax
void ets_write_char(char c);
Parameter
c
Type: char
Character to be printed.
Return Value
Type: void
See also: ets_install_putc1, ets_install_putc2, default_putc1, default_putc2
Used by:
ets_printf
Remarks
- Boot ROM function at
0x40001da0
- Registered driver addresses for putc1
0x3fffdd48
and putc20x3fffdd4c
are checked for null and skipped. - Assumes
character driver functions
have void returns. - If the
character driver functions
return the character they were given thenets_write_char
could be declared typeint
and the printed character would be returned. - Stack frame
ets_printf
Logic flow:
- Check if a putc1 or putc2 print function is installed. If not return 0.
- Check if a pre_printf callback is registered. If so call it.
- Call
ets_vprintf
withets_write_char
to print results.ets_write_char
will in turn call putc1 and putc2 if they are registered.
- Check if a post_printf callback is registered. If so call it.
- Return the return value given by
ets_vprintf
.
Curious comment in ESP8266_RTOS_SDK/components/esp8266/source/ets_printf.c
:
Re-write ets_printf in SDK side, since ets_printf in ROM will use a global variable which address is in heap region of SDK side. If use ets_printf in ROM, this variable may be re-write when heap alloc and modification.
What I think this means: Many Boot ROM functions have a single thread perspective.
There are often ROM functions that require a data area. These areas are statically
defined and cannot maintain a thread-specific context. The default_putc2
function
is a good example. In the Boot ROM data area, it stores a buffer
pointer and a length
value. The actual buffer
pointer and length
are supplied by the pre_printf callback function.
It uses these when adding print data to the buffer. After each character is printed, the buffer
pointer is advanced and the length is decremented. I see two things that can go wrong.
- A context switch while printing could corrupt the content of the buffer if a 2nd thread tried to print.
- Once finished the owning thread frees the memory; however, does not zero the
length
then any later printing will be writing to thebuffer
which is on the heap or allocated to a new thread.
I think similar issues exist for the single-threaded case where printing happens from an ISR, which in effect turns it into a two-thread case.
A lot is going on with this print option. If pre-printf and post-printf callbacks are registered they will be called. Only the registered putc1 and putc2 drivers are called on; however, if you supply NULL for the putc2 driver with ets_install_external_printf
you will get the default_putc2
driver registered.
As far as I can tell ets_printf
should be safe as long as you never call ets_install_external_printf
. Or call on something that calls it. And, that is a good question, is anyone calling? None that I have found so far.
Syntax
int ets_printf(const char *fmt, ...);
ets_install_external_printf
void ets_install_external_printf(
void pre_printtf_callback(char **buffer, uint16_t *length, void **context),
void _putc2(int c),
void post_printf_callback(void **context));
void pre_printf_callback(char **buffer, uint16_t *length, void **context);
char **buffer
is a pointer to the character buffer for storing the printf result.uint16_t *length
should be set to the length of the buffer.void **context
, optional, is supplied to the post-printf callback function.
void _putc2(int c);
Optional - a default putc2 driver will be supplied if this is NULL.
void post_printf_callback(void **context);
For a reliable system this option is required!
- Required if
pre_printf_callback
is supplied, but not inforced. - This function MUST reset buffer pointer and length to ensure no more writing to the buffer address occurs after this function exits! Failure to do so may result in heap corruption and other disasters.
Examples
size_t constexpr print_buf_sz = 128;
char *print_buf = NULL;
void pre_printf_cb(char **pC, uint16_t *len, void **pContext) {
if (print_buf) {
*pC = print_buf;
} else {
print_buf = *pC = (char *)zalloc(print_buf_sz);
}
if (print_buf) {
*pContext = (void *)pC;
*len = print_buf_sz - 1;
} else {
*len = 0; // Make sure there are no attempts to write
*pContext = NULL;
}
}
void post_printf_cb(void **pContext) {
if (print_buf) {
print_buf[print_buf_sz - est_get_printf_buf_remain_len() - 1] = '\0';
est_reset_printf_buf_len();
}
if (*pContext)
*pContext = NULL; // Zero the buffer pointer that putc2 has access to.
}
ets_install_external_printf(pre_printf_cb, NULL, post_printf_cb);
ets_printf("this is fun? :/\n");
ets_install_external_printf(NULL, NULL, NULL); // clear external prinf
... // Do something with print_buf,
// eg. copy to RTC_memory so you have the last error message
// generated before a crash.
free(print_buf);
print_buf = NULL:
Remarks
- calls to
ets_install_external_printf
must be done with IRQ disabled! ets_install_external_printf(NULL, NULL, NULL);
will not completely remove the effects of a previous install. Thedefault_putc2
driver gets installed for putc2 with this attempt to uninstall. To complete the operation you should finish withets_install_putc2(NULL);
.- at completion, the post-printf callback, must reset the buffer length to ensure no further use is made of the buffer pointer.
- Not included in SDK header files.
ets_external_printf
Do Not Use!
A printf function that only prints using the results from ets_install_external_printf
; however, it ignores the installed putc2 driver and use the internal default_putc2
driver.
It will call the registered pre and post printf callbacks.
Syntax
int ets_external_printf(...);
default_putc2
Default putc2 driver. Special driver captures output to a buffer until the buffer is full.
Buffer pointer setup and length gets initialized by the pre_printf_callback
function registered by a previous call to ets_install_external_printf`.
This functions is weird and does not appear to be used by the SDK. I suspect it has been grossly misunderstood :)
Syntax
int default_putc2(int c);
See Also
void est_reset_printf_buf_len
est_get_printf_buf_remain_len
ets_install_putc2
ets_printf
Remarks
- Boot ROM default putc2 driver function address
0x400024a8
. putc1 *((void *)0x3fffdd48) 3fffdd3c + 12
registered putc1 functionputc2 *((void *)0x3fffdd4c) 3fffdd3c + 16
registered putc2 functionpre CB *((uint32_t *)0x3fffdd50) 3fffdd3c + 20
1st argument ofets_install_external_printf
callpost CB *((uint32_t *)0x3fffdd54) 3fffdd3c + 24
3rd argument ofets_install_external_printf
callbuffer_len *((uint16_t *)0x3fffdd58) 3fffdd3c + 28
buffer_ptr *((uint32_t *)0x3fffdd5c) 3fffdd3c + 32
post CB data *((uint32_t *)0x3fffdd60) 3fffdd3c + 36
- no stack frame
est_reset_printf_buf_len
Reset available for write buffer length. No further writing will occur.
void est_reset_printf_buf_len(void);
Remarks
*((uint16_t *)0x3fffdd58) = 0; // buffer_len @ 3fffdd3c + 28
- The misspelling with "est" vs "ets" is from the
.ld
file. - Not included in SDK header files.
- no stack frame
est_get_printf_buf_remain_len
Get free space in buffer for putc2 to write.
Syntax
uint16_t est_get_printf_buf_remain_len(void);
Return Value
Type: unsigned short
number of bytes free in buffer
Remarks
return *((uint16_t *)0x3fffdd58); // buffer_len @ 3fffdd3c + 28
- The misspelling with "est" vs "ets" is from the
.ld
file. - Not included in SDK header files.
- no stack frame
Misc
rtc_get_reset_reason
Returns, the reason for the reset as reported by ROM code. Note, the reason values are different from those returned by the NONOS SDK. Also, the reset reason will not change after a software WDT reset or software reset. For example, if the first reset was caused by a power-on boot, the reset reason is 1. After a software reset, the reset reason will still be 1.
Syntax
extern "C" uint32_t rtc_get_reset_reason(void);
Return Value
Type: uint32_t
typedef enum {
NO_MEAN = 0, /* Undefined */
POWERON_RESET = 1, /* Power on boot */ /*<1, Vbat power on reset*/
EXT_RESET = 2, /* External reset or wake-up from Deep-sleep */ /**<2, external system reset*/
SW_RESET = 3, /**/ /**<3, Software reset digital core*/
OWDT_RESET = 4, /* Hardware WDT reset *//**<4, Legacy watch dog reset digital core*/
DEEPSLEEP_RESET = 5, /**//**<5, Deep Sleep reset digital core*/
SDIO_RESET = 6, /**//**<6, Reset by SLC module, reset digital core*/
} RTC_RESET_REASON;
See Also
- "ESP8266 Reset Causes and Common Fatal Exception Causes" - 1.1. Identifying Reset Cause in ROM Code
Remarks
- Boot ROM function.
- Prototype from ESP8266_RTOS_SDK
- Gets the value from RTC memory. *(0x60000714) & 0x0F
ets_delay_us
This function delays for a specified period in microseconds. For a 160MHz CPU clock rate, the maximum delay value is 26,843,545, around 26 seconds. The delay value is converted to CPU cycles. Then the function spins in a tight loop, referencing the ESP8266's CCOUNT
register, waiting for it to equal or exceed the difference.
Syntax
void ets_delay_us(uint32_t usec);
Parameter
Type: uint32_t
Return Value
Type: void
Example
ets_delay_us(1000);
Remarks
- none
See Also
- none