Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ set(srcs
src/mem_utils.c
src/rom_wrappers.c
src/security.c
src/uart.c
)

if(STUB_LOG_ENABLED IN_LIST STUB_COMPILE_DEFS)
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ A complete example project is provided in the [example](example/) directory. It

See the [example README](example/README.md) for build instructions.

## Function Naming Conventions

Functions that contain `rominit` (e.g., `stub_target_uart_rominit_intr_attach`) require ROM preinitialization by entering download mode to work correctly. These functions are simplified compared to full implementation and expect some initialization done by ROM. They should only be called in contexts where the ROM download mode has been entered.

## Project Structure

The library uses a three-layer architecture to eliminate circular dependencies and maximize code reuse:
Expand Down
19 changes: 19 additions & 0 deletions example/stub_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <esp-stub-lib/err.h>
#include <esp-stub-lib/mem_utils.h>
#include <esp-stub-lib/security.h>
#include <esp-stub-lib/uart.h>

#include "stub_main.h"

Expand Down Expand Up @@ -62,6 +63,24 @@ static void example_security(void)
}
}

static int __attribute__((unused)) handle_test_uart(void)
{
void *uart_rx_interrupt_handler = NULL;
stub_lib_uart_wait_idle(UART_NUM_0);

stub_lib_uart_init(UART_NUM_0);
stub_lib_uart_set_rx_timeout(UART_NUM_0, 10);
(void)stub_lib_uart_get_rxfifo_count(UART_NUM_0);
(void)stub_lib_uart_get_intr_flags(UART_NUM_0);
(void)stub_lib_uart_read_rxfifo_byte(UART_NUM_0);
(void)stub_lib_uart_rx_one_char();
stub_lib_uart_rominit_intr_attach(UART_NUM_0, 5, uart_rx_interrupt_handler, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
stub_lib_uart_tx_one_char('A');
stub_lib_uart_tx_flush();

return 0;
}

static __attribute__((unused)) int handle_test1(va_list ap)
{
(void)ap;
Expand Down
20 changes: 0 additions & 20 deletions include/esp-stub-lib/rom_wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,6 @@ void stub_lib_delay_us(uint32_t us);
*/
uint16_t stub_lib_crc16_le(uint16_t crc, const uint8_t *buf, uint32_t len);

/**
* @brief Transmit a single byte over UART.
*
* @param c Byte to transmit.
* @return 0 if successful, non-zero if error occurred.
*/
uint8_t stub_lib_uart_tx_one_char(uint8_t c);

/**
* @brief Receive a single byte (blocking) from UART.
*
* @return Received byte.
*/
uint8_t stub_lib_uart_rx_one_char(void);

/**
* @brief Flush any buffered transmit data.
*/
void stub_lib_uart_tx_flush(void);

#ifdef __cplusplus
}
#endif // __cplusplus
147 changes: 147 additions & 0 deletions include/esp-stub-lib/uart.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include <soc/soc_caps.h>

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

/**
* @brief UART port number type
*
* Available UART ports depend on the chip's SOC_UART_HP_NUM capability.
* Use the UART_NUM_* enum values (e.g., UART_NUM_0, UART_NUM_1, etc.)
*/

typedef enum {
UART_NUM_0, /*!< UART port 0 */
UART_NUM_1, /*!< UART port 1 */
#if SOC_UART_HP_NUM > 2
UART_NUM_2, /*!< UART port 2 */
#endif
#if SOC_UART_HP_NUM > 3
UART_NUM_3, /*!< UART port 3 */
#endif
#if SOC_UART_HP_NUM > 4
UART_NUM_4, /*!< UART port 4 */
#endif
UART_NUM_MAX, /*!< UART port max */
} uart_port_t;

#define UART_INTR_RXFIFO_FULL (0x1 << 0)
#define UART_INTR_RXFIFO_TOUT (0x1 << 8)

/**
* @brief Wait until UART is idle
*
* @param uart_num UART port number
*/
void stub_lib_uart_wait_idle(uart_port_t uart_num);

/**
* @brief Initialize UART
*
* @param uart_num UART port number
*/
void stub_lib_uart_init(uart_port_t uart_num);

/**
* @brief Set UART baud rate
*
* @param uart_num UART port number
* @param baudrate Baud rate
*/
void stub_lib_uart_rominit_set_baudrate(uart_port_t uart_num, uint32_t baudrate);

/**
* @brief Attach interrupt handler to UART and configure interrupts
*
* @note This function requires ROM preinit (download mode initialization) to work correctly.
* It uses ROM functions (ets_isr_attach, ets_isr_unmask) that are only available
* after the ROM has entered download mode and completed its initialization.
* Functions that contain "rominit" have this requirement.
*
* @param uart_num UART port number
* @param intr_num CPU interrupt source
* @param handler Interrupt handler function pointer
* @param flags Interrupt enable flags (bitwise OR of UART_INTR_* values)
*/
void stub_lib_uart_rominit_intr_attach(uart_port_t uart_num, int intr_num, void *handler, uint32_t flags);

/**
* @brief Get and clear UART interrupt status
*
* This function returns the interrupt status and automatically clears it.
* Call this at the beginning of your interrupt handler.
*
* @param uart_num UART port number
* @return Bitmask of active interrupts that were cleared
*/
uint32_t stub_lib_uart_get_intr_flags(uart_port_t uart_num);

/**
* @brief Get number of bytes available in RX FIFO
*
* @param uart_num UART port number
* @return Number of bytes currently in the RX FIFO
*/
uint32_t stub_lib_uart_get_rxfifo_count(uart_port_t uart_num);

/**
* @brief Read a single byte from UART RX FIFO
*
* This function reads one byte from the RX FIFO without checking
* if data is available. Call stub_lib_uart_get_rxfifo_count()
* first to ensure data is available.
*
* @param uart_num UART port number
* @return The byte read from the FIFO
*/
uint8_t stub_lib_uart_read_rxfifo_byte(uart_port_t uart_num);

/**
* @brief Configure RX timeout threshold
*
* @param uart_num UART port number
* @param timeout Timeout value in bit times (1-126, 0 to disable)
* timeout mean the multiple of UART packets (~11 bytes) that can be received before timeout
*/
void stub_lib_uart_set_rx_timeout(uart_port_t uart_num, uint8_t timeout);

/**
* @brief Transmit a single byte over UART.
*
* @note This function operates on the console UART (set via stub_lib_uart_set_as_console).
* It does not take a uart_num parameter.
*
* @param c Byte to transmit.
* @return 0 if successful, non-zero if error occurred.
*/
uint8_t stub_lib_uart_tx_one_char(uint8_t c);

/**
* @brief Receive a single byte (blocking) from UART.
*
* @note This function operates on the console UART (set via stub_lib_uart_set_as_console).
* It does not take a uart_num parameter.
*
* @return Received byte.
*/
uint8_t stub_lib_uart_rx_one_char(void);

/**
* @brief Flush any buffered transmit data.
*/
void stub_lib_uart_tx_flush(void);

#ifdef __cplusplus
}
#endif // __cplusplus
2 changes: 1 addition & 1 deletion src/log_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extern void ets_install_uart_printf(void);

void stub_lib_log_init()
{
stub_target_uart_init(0, 115200);
stub_target_uart_init(0);
ets_install_putc1(NULL);
ets_install_putc2(NULL);
ets_install_uart_printf();
Expand Down
18 changes: 0 additions & 18 deletions src/rom_wrappers.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@

extern void ets_delay_us(uint32_t us);
extern uint16_t crc16_le(uint16_t crc, const uint8_t *buf, uint32_t len);
extern uint8_t uart_tx_one_char(uint8_t ch);
extern uint8_t uart_rx_one_char(void);
extern void uart_tx_flush(void);

void stub_lib_delay_us(uint32_t us)
{
Expand All @@ -20,18 +17,3 @@ uint16_t stub_lib_crc16_le(uint16_t crc, const uint8_t *buf, uint32_t len)
{
return crc16_le(crc, buf, len);
}

uint8_t stub_lib_uart_tx_one_char(uint8_t c)
{
return uart_tx_one_char(c);
}

uint8_t stub_lib_uart_rx_one_char(void)
{
return uart_rx_one_char();
}

void stub_lib_uart_tx_flush(void)
{
uart_tx_flush();
}
45 changes: 44 additions & 1 deletion src/target/base/include/target/uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,48 @@
#pragma once

#include <stdint.h>
#include <stdbool.h>

void stub_target_uart_init(uint8_t uart_num, uint32_t baudrate);
/**
* @brief Wait until UART is idle
*
* @param uart_num UART port number
*/
void stub_target_uart_wait_idle(uint8_t uart_num);

/**
* @brief Initialize UART
*
* @param uart_num UART port number
*/
void stub_target_uart_init(uint8_t uart_num);

/**
* @brief Set UART baud rate
*
* @param uart_num UART port number
* @param baudrate Baud rate
*/
void stub_target_uart_rominit_set_baudrate(uint8_t uart_num, uint32_t baudrate);

/**
* @brief Flush any buffered transmit data.
*/
void stub_target_uart_tx_flush(void);

/**
* @brief Wrapper for esp_rom_uart_attach
*
* Each target must implement this to adapt to its ROM function signature.
* @param rxBuffer RX buffer pointer (may be ignored by some targets)
*/
void stub_target_rom_uart_attach(void *rxBuffer);

/**
* @brief Wrapper for esp_rom_uart_init
*
* Each target must implement this to adapt to its ROM function signature.
* @param uart_no UART number
* @param clock Clock frequency (may be calculated internally by some targets)
*/
void stub_target_rom_uart_init(uint8_t uart_no, uint32_t clock);
36 changes: 33 additions & 3 deletions src/target/common/src/uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,42 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/

#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <target/uart.h>

void __attribute__((weak)) stub_target_uart_init(uint8_t uart_num, uint32_t baudrate)
extern void esp_rom_set_cpu_ticks_per_us(uint32_t ticks_per_us);
extern uint32_t esp_rom_get_cpu_freq(void);
extern void esp_rom_uart_set_as_console(uint8_t uart_no);
extern void esp_rom_uart_tx_wait_idle(uint8_t uart_num);
extern void esp_rom_uart_div_modify(uint8_t uart_no, uint32_t divisor);
extern void esp_rom_uart_flush_tx(void);

void __attribute__((weak)) stub_target_uart_wait_idle(uint8_t uart_num)
{
esp_rom_uart_tx_wait_idle(uart_num);
}

void __attribute__((weak)) stub_target_uart_init(uint8_t uart_num)
{
esp_rom_set_cpu_ticks_per_us(esp_rom_get_cpu_freq() / 1000000);
stub_target_rom_uart_attach(NULL);
uint32_t clock = esp_rom_get_cpu_freq();
stub_target_rom_uart_init(uart_num, clock);
esp_rom_uart_set_as_console(uart_num);
}

void __attribute__((weak)) stub_target_uart_rominit_set_baudrate(uint8_t uart_num, uint32_t baudrate)
{
uint32_t clock = esp_rom_get_cpu_freq() << 4;
uint32_t divisor = clock / baudrate;
stub_target_uart_wait_idle(uart_num);
esp_rom_uart_div_modify(uart_num, divisor);
// TODO: Consider decimal part
}

void __attribute__((weak)) stub_target_uart_tx_flush(void)
{
(void)baudrate;
(void)uart_num;
esp_rom_uart_flush_tx();
}
2 changes: 2 additions & 0 deletions src/target/esp32/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ add_library(${ESP_TARGET_LIB} STATIC ${srcs})

target_link_options(${ESP_TARGET_LIB}
PUBLIC -T${CMAKE_CURRENT_LIST_DIR}/ld/esp32.rom.ld
PUBLIC -T${CMAKE_CURRENT_LIST_DIR}/ld/esp32.rom.redefined.ld
PUBLIC -T${CMAKE_CURRENT_LIST_DIR}/ld/esp32.rom.libc-funcs.ld
PUBLIC -T${CMAKE_CURRENT_LIST_DIR}/ld/esp32.rom.libgcc.ld
PUBLIC -T${CMAKE_CURRENT_LIST_DIR}/ld/esp32.rom.api.ld
)
3 changes: 3 additions & 0 deletions src/target/esp32/include/soc/soc_caps.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

#pragma once

/*-------------------------- COMMON CAPS ---------------------------------------*/
#define SOC_UART_HP_NUM (3) /*!< HP UART number */

// Memory Caps
#define SOC_RTC_FAST_MEM_SUPPORTED 1
#define SOC_RTC_SLOW_MEM_SUPPORTED 1
Expand Down
Loading