universalisos/hal/espressif/components/upper_hal_pcnt
Fábio Coutada 98ed638f3c WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules
All uncommitted work from ESP32 GDB stub development session.
Includes:
- GDB stub (uos_gdbstub.c/.h/_entry.S)
- Startup vector table rewrite
- ESP32 HAL integration files
- Boot chain design docs
- AGENTS.md absolute rules (commit before refactor, no unauthorized changes)
- All prior deepseek session work

This commit prevents further data loss. No claims of correctness.
2026-07-17 01:36:58 +01:00
..
include/driver WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules 2026-07-17 01:36:58 +01:00
src WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules 2026-07-17 01:36:58 +01:00
test_apps WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules 2026-07-17 01:36:58 +01:00
CMakeLists.txt WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules 2026-07-17 01:36:58 +01:00
Kconfig WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules 2026-07-17 01:36:58 +01:00
linker.lf WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules 2026-07-17 01:36:58 +01:00
README.md WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules 2026-07-17 01:36:58 +01:00

PCNT Driver Design

Concurrency

The count value and the overflow state of the count value are located in different registers, resulting in the software being unable to obtain information from both of them in the same read instruction.

The race condition case is as follow:

sequenceDiagram
    participant HW as PCNT Hardware
    participant CPU0_ISR as CPU0_ISR
    participant CPU1_Task as CPU1_Task (pcnt_unit_get_count)
    participant REG as Reg and Soft accum counter State

    CPU1_Task->>CPU1_Task: Call pcnt_unit_get_count()
    Note over REG: intr_status = 0<br/>cnt_reg = cnt_value<br/>accum_value = old_value
    CPU1_Task->>CPU1_Task: portENTER_CRITICAL_SAFE()
    CPU1_Task->>REG: Read intr_status
    Note over CPU1_Task: intr_status=0, no need to do compensation
    HW->>REG: Overflow interrupt triggered
    Note over REG: intr_status = 1<br/>cnt_reg = 0<br/>accum_value = old_value
    REG->>CPU0_ISR: ISR is called
    CPU0_ISR->>CPU0_ISR: try portENTER_CRITICAL_SAFE() but spin

    CPU1_Task->>REG: Read cnt_reg(0) + accum_value(old)
    CPU1_Task->>CPU1_Task: portEXIT_CRITICAL_SAFE()

    CPU0_ISR->>CPU0_ISR: portENTER_CRITICAL_SAFE()
    CPU0_ISR->>REG: Clear interrupt status and update accum_value
    Note over REG: intr_status = 0<br/>accum_value = new_value
    CPU0_ISR->>CPU0_ISR: portEXIT_CRITICAL_SAFE()
    
    Note over CPU0_ISR: Process events
    Note over CPU1_Task: Return incorrect count ❌

In the software, we determine whether to perform compensation by checking whether the count value exceeds half of the limit. This can prevent counting errors when the overflow frequency is not high.