Skip to content

am243x: add pru-eqep example#149

Open
a1248924 wants to merge 1 commit into
mainfrom
a1248924_EQEP
Open

am243x: add pru-eqep example#149
a1248924 wants to merge 1 commit into
mainfrom
a1248924_EQEP

Conversation

@a1248924

@a1248924 a1248924 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Signed-off-by: Ayushman a-ayushman@ti.com

@qodo-code-review

qodo-code-review Bot commented Jul 3, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (7) 📘 Rule violations (4) 📜 Skill insights (0)

Grey Divider


Action required

1. sbbo before buffer bounds 📘 Rule violation ⛨ Security
Description
sbbo &time_stamp, DMEM0, buffer_addr, 4 stores to DMEM using buffer_addr before verifying that
buffer_addr + 4 stays within buffer_size, and the wrap check only triggers on `buffer_addr >
buffer_size (allowing buffer_addr == buffer_size`). This can write past the intended buffer end
and corrupt adjacent PRU memory.
Code

examples/pru_eqep/firmware/main.asm[R122-129]

+    sbbo    &time_stamp, DMEM0, buffer_addr, 4
+    sbco    &buffer_addr, DMEM1, WRITE_PTR_OFFSET, 2
+    sbco    &QPOS, DMEM1, CH_POS_OFFSET, 4
+
+    ; Handle buffer wrap-around
+    qbgt    no_buffer_wrap, buffer_addr, buffer_size
+    ldi     buffer_addr, 0
+    ldi     READ_POS_BUFF, 0
Evidence
PR Compliance ID 1 requires bounds checks before memory accesses and specifically calls out
offset+size validation. In main.asm, a 4-byte store uses buffer_addr before the wrap/bounds
logic, and the wrap condition (qbgt) does not prevent buffer_addr == buffer_size, enabling an
out-of-bounds write.

examples/pru_eqep/firmware/main.asm[122-129]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PRU firmware writes a 4-byte timestamp to `DMEM0 + buffer_addr` before ensuring `buffer_addr` is within the valid buffer range, and the wrap condition allows `buffer_addr == buffer_size`.

## Issue Context
Compliance requires bounds checks for memory accesses, including validating `offset + size` against named limits.

## Fix Focus Areas
- examples/pru_eqep/firmware/main.asm[122-129]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Direct .asg register aliases 📘 Rule violation ⚙ Maintainability
Description
The new PRU assembly include uses extensive direct register aliasing via .asg instead of
.struct/.sassign. This increases risk of register overlap/corruption and makes calling/register
conventions harder to audit.
Code

examples/pru_eqep/firmware/include/memory.inc[R33-49]

+    .asg   r0,     scratch
+    .asg   r1,     scratch2
+    .asg   r17.b0, curr_gpi_sample  ; Current GPI state after masking
+    .asg   r17.b1, A_B_Z_edge       ; Edge detection for A,B,Z signals
+    .asg   r18,    curr_ts          ; Timestamp counter
+    .asg   r19.b0, edge             ; Edge detection
+    .asg   r20.b0, prev_sample      ; Previous state of A,B,Z signals
+    .asg   r21,    time_stamp       ; Time stamp counter
+    .asg   r22.w0, buffer_addr      ; Buffer address pointer
+    .asg   r22.w2, buffer_size      ; Buffer size
+    .asg   r23,    wr_ptr           ; Write pointer
+    .asg   r24.w0, READ_POS_BUFF    ; Read position buffer
+    .asg   r24.w2, read_pos_speed_intr ; Read position speed interrupt
+    .asg   r26.b0, a_b_transition   ; A/B transition state
+    .asg   r26.b1, qpos_update      ; 2-> QPOS--, 1-> QPOS++, 0-> no change
+    .asg   r27,    QPOS             ; Position counter
+    .asg   r28,    DMEM0            ; Data memory pointer
Evidence
PR Compliance ID 6 requires new/updated PRU assembly to prefer .struct/.sassign over direct
aliasing. The added memory.inc defines most register usage using .asg (e.g., scratch,
buffer_addr, QPOS), meeting the failure criteria for direct register aliasing.

examples/pru_eqep/firmware/include/memory.inc[33-49]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PRU assembly register map is implemented with many `.asg` aliases, contrary to the preferred `.struct`/`.sassign` pattern.

## Issue Context
The compliance rule requires structured register allocation to reduce overlap/corruption risk and improve maintainability.

## Fix Focus Areas
- examples/pru_eqep/firmware/include/memory.inc[33-49]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. readme.md wrong empty path 📘 Rule violation ≡ Correctness
Description
The README references <open-pru/examples/empty/firmware/device/>, which does not match this
example’s actual location and can mislead users. The same section also contains extremely long
markdown lines, reducing portability/readability in rendered views and diffs.
Code

examples/pru_eqep/readme.md[R20-23]

+- **When using CCS projects to build**, import the CCS project from the above mentioned Example folder path for R5F and PRU, After this `main.asm`, `linker.cmd` files gets copied to ccs workspace of PRU project. The `main.asm` contains sample code to halt PRU program
+
+     - Build the PRU project using the CCS project menu (see [for AM64x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/latest/exports/docs/api_guide_am64x/CCS_PROJECTS_PAGE.html), [for AM243x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM243X/latest/exports/docs/api_guide_am243x/CCS_PROJECTS_PAGE.html), [for AM261x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM261X/latest/exports/docs/api_guide_am261x/CCS_PROJECTS_PAGE.html), [for AM263x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM263X/latest/exports/docs/api_guide_am263x/CCS_PROJECTS_PAGE.html), [for AM263Px](https://software-dl.ti.com/mcu-plus-sdk/esd/AM263PX/latest/exports/docs/api_guide_am263px/CCS_PROJECTS_PAGE.html)).
+          - Build Flow: Once you click on build in PRU project, firmware header file which is generated in release or debug folder of ccs workspace, is moved to  `<open-pru/examples/empty/firmware/device/>`
Evidence
PR Compliance ID 13 requires repository path references in docs to match the on-disk layout; the
README points to examples/empty despite this being the pru_eqep example. PR Compliance ID 15
requires respecting markdown line-length limits; the same section contains very long single-line
link text and prose.

examples/pru_eqep/readme.md[20-23]
Best Practice: Learned patterns
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new README contains an incorrect repository path reference (points to `examples/empty` instead of `examples/pru_eqep`) and includes very long single-line markdown content.

## Issue Context
Incorrect path references break portability/usability of docs, and overly long markdown lines violate documentation formatting expectations.

## Fix Focus Areas
- examples/pru_eqep/readme.md[20-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (6)
4. Asm file included by make 🐞 Bug ≡ Correctness
Description
The PRU0 firmware makefile uses GNU make include to include memory.inc, but that file contains
assembly syntax (e.g., ; comments and .asg) and will be parsed as make syntax, causing the build
to fail.
Code

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/makefile[R31-34]

+# pru_rules.mak has shared settings for all PRU/RTU/TX_PRU core makefiles
+include $(OPEN_PRU_PATH)/pru_rules.mak
+include $(OPEN_PRU_PATH)/examples/pru_eqep/firmware/include/memory.inc
+# Optional:
Evidence
The makefile explicitly includes memory.inc, and memory.inc begins with ; and assembly
directives which are not valid makefile syntax, so make will error when trying to parse it.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/makefile[31-38]
examples/pru_eqep/firmware/include/memory.inc[1-8]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/makefile` includes an assembly `.inc` file via make's `include`, which will break parsing and stop the build.

### Issue Context
`memory.inc` is meant to be included by the PRU assembler (via `.include` in `main.asm` or via compiler include paths), not by `make`.

### Fix Focus Areas
- Remove the `include $(OPEN_PRU_PATH)/examples/pru_eqep/firmware/include/memory.inc` line.
- Ensure the assembler can still find `memory.inc` via `INCLUDE += --include_path=...`.

### Fix Focus Areas (code references)
- examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/makefile[31-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Wrong GPI byte sampled 🐞 Bug ≡ Correctness
Description
memory.inc defines cur_sample as r31.b0 for PRU0/PRU1 and TX_PRU cores, but those cores should
sample GPIs from r31.b1 and r31.b2 respectively; the firmware will read incorrect A/B/Z inputs
and compute wrong position/speed.
Code

examples/pru_eqep/firmware/include/memory.inc[R51-58]

+; Define cur_sample based on core type
+    .if $isdefed("RTU_PRU0") | $isdefed("RTU_PRU1")
+        .asg   r31.b0, cur_sample   ; Use byte0 for GPIs 0-7
+    .elseif $isdefed("PRU0") | $isdefed("PRU1")
+        .asg   r31.b0, cur_sample   ; Use byte1 for GPIs 8-15
+    .elseif $isdefed("TX_PRU0") | $isdefed("TX_PRU1")
+        .asg   r31.b0, cur_sample   ; Use byte2 for GPIs 16-19
+    .endif
Evidence
The implementation maps PRU0/PRU1 and TX_PRU cores to r31.b0, while the file’s own guide states
PRU cores must use r31.b1 and TX_PRU cores must use r31.b2.

examples/pru_eqep/firmware/include/memory.inc[51-58]
examples/pru_eqep/firmware/include/memory.inc[154-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`cur_sample` is aliased to the wrong r31 byte for PRU and TX_PRU cores, so the firmware polls the wrong GPI bits.

### Issue Context
The same file documents the intended mapping (b0 for GPIs 0-7, b1 for 8-15, b2 for 16-19), but the implementation always uses `r31.b0`.

### Fix Focus Areas
- Use `r31.b1` for `PRU0`/`PRU1`.
- Use `r31.b2` for `TX_PRU0`/`TX_PRU1`.

### Fix Focus Areas (code references)
- examples/pru_eqep/firmware/include/memory.inc[51-58]
- examples/pru_eqep/firmware/include/memory.inc[154-167]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Buffer wrap condition reversed 🐞 Bug ≡ Correctness
Description
In main.asm, the wrap check qbgt no_buffer_wrap, buffer_addr, buffer_size causes the code to
take the wrap-around path whenever buffer_addr <= buffer_size, so buffer_addr is repeatedly
reset and the normal increment path is skipped.
Code

examples/pru_eqep/firmware/main.asm[R126-129]

+    ; Handle buffer wrap-around
+    qbgt    no_buffer_wrap, buffer_addr, buffer_size
+    ldi     buffer_addr, 0
+    ldi     READ_POS_BUFF, 0
Evidence
The branch guards the wrap reset, while the increment occurs only in the no_buffer_wrap block;
with buffer_addr starting at 0, the current condition falls through to the wrap reset and never
reaches the increment path.

examples/pru_eqep/firmware/main.asm[120-130]
examples/pru_eqep/firmware/main.asm[156-159]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The PRU ring-buffer wrap logic branches incorrectly, so the firmware resets the buffer pointers at the wrong time.

### Issue Context
The increment of `buffer_addr` happens in `no_buffer_wrap`, but the current branch condition makes it unreachable for normal values (e.g., `buffer_addr` starts at 0).

### Fix Focus Areas
- Change the comparison so that `no_buffer_wrap` is taken when `buffer_addr < buffer_size` (or when `buffer_addr` is within bounds).
- Ensure wrap occurs only when the next write would exceed the buffer (>= size).

### Fix Focus Areas (code references)
- examples/pru_eqep/firmware/main.asm[126-130]
- examples/pru_eqep/firmware/main.asm[156-159]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Mask used as register offset 🐞 Bug ≡ Correctness
Description
ABZ_enable_load_share_mode() adds *_MASK constants to the PRU CFG base pointer when reading the
EDPRU TX CFG register, but those constants are bitmasks (e.g., 0x10) rather than register offsets
(e.g., 0xE4/0x104), so it reads the wrong address and may misconfigure load-share mode.
Code

examples/pru_eqep/mcuplus/pru_eqep_example.c[R569-583]

+void ABZ_enable_load_share_mode(void *pruCfg, uint32_t pruSlice)
+{
+    uint32_t regVal;
+    if(pruSlice == 1)
+    {
+        regVal = HW_RD_REG32((uint8_t *)pruCfg + CSL_ICSSCFG_EDPRU1TXCFGREGISTER_PRU1_ED_TX_CLK_SEL_MASK);
+        regVal |= CSL_ICSSCFG_EDPRU1TXCFGREGISTER_PRU1_ENDAT_SHARE_EN_MASK;
+        HW_WR_REG32((uint8_t *)pruCfg + CSL_ICSSCFG_EDPRU1TXCFGREGISTER, regVal);
+    }
+    else
+    {
+        regVal = HW_RD_REG32((uint8_t *)pruCfg + CSL_ICSSCFG_EDPRU0TXCFGREGISTER_PRU0_ED_TX_CLK_SEL_MASK);
+        regVal |= CSL_ICSSCFG_EDPRU0TXCFGREGISTER_PRU0_ENDAT_SHARE_EN_MASK;
+        HW_WR_REG32((uint8_t *)pruCfg + CSL_ICSSCFG_EDPRU0TXCFGREGISTER, regVal);
+    }
Evidence
The host code uses *_MASK macros as pointer offsets, but the CSL header shows those macros are bit
masks while the register offsets are separate macros (0xE4/0x104).

examples/pru_eqep/mcuplus/pru_eqep_example.c[569-583]
source/include/am243x/cslr_icss_g.h[13069-13071]
source/include/am243x/cslr_icss_g.h[14785-14789]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The code reads a PRU CFG register using a bitmask macro as if it were a register offset, so the value read is from the wrong location.

### Issue Context
`CSL_ICSSCFG_EDPRU1TXCFGREGISTER_PRU1_ED_TX_CLK_SEL_MASK` is 0x10 (mask), while `CSL_ICSSCFG_EDPRU1TXCFGREGISTER` is 0x104 (offset). The same pattern exists for PRU0.

### Fix Focus Areas
- Replace the `HW_RD_REG32(base + *_MASK)` reads with `HW_RD_REG32(base + CSL_ICSSCFG_EDPRU{0,1}TXCFGREGISTER)`.
- Keep using the `*_ENDAT_SHARE_EN_MASK` bitmask when setting/clearing bits in the read value.

### Fix Focus Areas (code references)
- examples/pru_eqep/mcuplus/pru_eqep_example.c[569-583]
- source/include/am243x/cslr_icss_g.h[13069-13071]
- source/include/am243x/cslr_icss_g.h[14785-14789]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Position base uses CH0 offset 🐞 Bug ≡ Correctness
Description
All six channels set position_base using CH0_POSITION_OFFSET, ignoring the distinct offsets
defined for channels 1-5, so channels 1-5 will read the wrong position value and report incorrect
direction/position.
Code

examples/pru_eqep/mcuplus/pru_eqep_example.c[R317-322]

+    ABZHandle[0]->position_base = (void *)((uint32_t)(ABZHandle[0]->baseMemAddr1) + CH0_POSITION_OFFSET);
+    ABZHandle[1]->position_base = (void *)((uint32_t)(ABZHandle[1]->baseMemAddr1) + CH0_POSITION_OFFSET);
+    ABZHandle[2]->position_base = (void *)((uint32_t)(ABZHandle[2]->baseMemAddr1) + CH0_POSITION_OFFSET);
+    ABZHandle[3]->position_base = (void *)((uint32_t)(ABZHandle[3]->baseMemAddr1) + CH0_POSITION_OFFSET);
+    ABZHandle[4]->position_base = (void *)((uint32_t)(ABZHandle[4]->baseMemAddr1) + CH0_POSITION_OFFSET);
+    ABZHandle[5]->position_base = (void *)((uint32_t)(ABZHandle[5]->baseMemAddr1) + CH0_POSITION_OFFSET);
Evidence
The code assigns CH0_POSITION_OFFSET for all channels, while the file defines unique offsets per
channel; this guarantees that channels 1–5 read the wrong address.

examples/pru_eqep/mcuplus/pru_eqep_example.c[67-73]
examples/pru_eqep/mcuplus/pru_eqep_example.c[317-322]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`position_base` is initialized with the same offset for every channel, even though each channel has a different position offset.

### Issue Context
The file defines `CH1_POSITION_OFFSET` ... `CH5_POSITION_OFFSET`, but they are not used when setting `ABZHandle[i]->position_base`.

### Fix Focus Areas
- Set `position_base` for each channel using the matching `CHx_POSITION_OFFSET`.
- Optionally use a small lookup array to avoid repetitive code.

### Fix Focus Areas (code references)
- examples/pru_eqep/mcuplus/pru_eqep_example.c[67-73]
- examples/pru_eqep/mcuplus/pru_eqep_example.c[317-322]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. mem_limit scaled by channel 🐞 Bug ≡ Correctness
Description
mem_limit is computed as DMEM_CHANNEL_SIZE*i + DMEM_CHANNEL_SIZE, but the PRU firmware write
pointer (buffer_addr) is a per-channel offset that wraps to 0; for channels >0 this inflates
wrap-around edge counts and produces incorrect speed.
Code

examples/pru_eqep/mcuplus/pru_eqep_example.c[R309-314]

+        ABZHandle[i]->QPOSCOUNT = 0;
+        ABZHandle[i]->read_ptr = ABZHandle[i]->baseMemAddr0;
+        ABZHandle[i]->write_ptr = ABZHandle[i]->baseMemAddr1;
+        ABZHandle[i]->mem_limit=(DMEM_CHANNEL_SIZE)*(i) + DMEM_CHANNEL_SIZE;
+        if(i<3) ABZHandle[i]->pru_slice=0;
+        else ABZHandle[i]->pru_slice=1;
Evidence
The host’s wrap calculation uses mem_limit as the end of the ring buffer, but firmware resets the
write pointer to 0 on wrap; scaling mem_limit by channel index therefore over-counts edges for
channels >0.

examples/pru_eqep/mcuplus/pru_eqep_example.c[294-316]
examples/pru_eqep/mcuplus/pru_eqep_example.c[485-510]
examples/pru_eqep/firmware/main.asm[126-130]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Speed calculation uses `mem_limit` as the buffer end when the write pointer wraps, but `mem_limit` is incorrectly made larger for higher channel indices.

### Issue Context
`EQEP_Get_Speed_ABZ()` uses `mem_limit` only as an upper bound for `(mem_limit - read_ptr_offset + write_ptr_offset)` when `read_ptr_offset > write_ptr_offset` (wrap case). Since firmware resets `buffer_addr` back to 0 on wrap, `mem_limit` must be the fixed per-channel buffer size.

### Fix Focus Areas
- Change `ABZHandle[i]->mem_limit` to `DMEM_CHANNEL_SIZE` (or the correct per-channel buffer size constant), not scaled by `i`.
- Ensure the host-side buffer size constant matches the firmware’s `MAX_BUFFER`/wrap behavior.

### Fix Focus Areas (code references)
- examples/pru_eqep/mcuplus/pru_eqep_example.c[294-316]
- examples/pru_eqep/mcuplus/pru_eqep_example.c[485-510]
- examples/pru_eqep/firmware/main.asm[126-130]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

10. Long postBuildStep command line 📘 Rule violation ⚙ Maintainability
Description
New .projectspec files embed long command strings in a single line (for example,
postBuildStep=...), which harms readability and violates the repository’s configuration
line-length expectations. This makes future edits and reviews error-prone.
Code

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/example.projectspec[R55-57]

+        postBuildStep="
+            $(CG_TOOL_ROOT)/bin/hexpru --diag_wrap=off --array --array:name_prefix=PRU0Firmware -o ../pru0_load_bin.h ${BuildArtifactFileBaseName}.out;
+        "
Evidence
PR Compliance ID 17 requires long command-like attributes in config files to be wrapped/reflowed
where feasible. The added postBuildStep entries are long single lines in multiple new
.projectspec files, matching the failure criteria.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/example.projectspec[55-57]
examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/example.projectspec[70-70]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Project configuration includes very long single-line command attributes (notably `postBuildStep`), reducing maintainability and violating line-length guidance.

## Issue Context
The compliance rule requests wrapping/reflowing long command-like attributes where feasible.

## Fix Focus Areas
- examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/example.projectspec[55-57]
- examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/example.projectspec[70-70]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Spin loop inside ISR 🐞 Bug ☼ Reliability
Description
Each PRU host interrupt ISR busy-waits in a tight while (...) ; loop until the PRU updates the
write pointer; if the PRU stalls or the pointer never changes, the ISR can run indefinitely and
block other interrupts/tasks.
Code

examples/pru_eqep/mcuplus/pru_eqep_example.c[R394-401]

+    if (!(ABZHandle[0]->write_ptr_offset == (HW_RD_REG32(ABZHandle[0]->baseMemAddr1) & WRITE_PTR_OFFSET_MASK)))
+    {
+        EQEP_Get_Speed_ABZ(0);
+    }
+    else
+    {
+        while (ABZHandle[0]->write_ptr_offset == (HW_RD_REG32(ABZHandle[0]->baseMemAddr1) & WRITE_PTR_OFFSET_MASK));
+        EQEP_Get_Speed_ABZ(0);
Evidence
The ISR code contains a tight polling loop with no timeout, which is unsafe in Hwi context and can
prevent forward progress under fault conditions.

examples/pru_eqep/mcuplus/pru_eqep_example.c[389-420]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The interrupt handlers contain an unbounded spin-wait, which can lock the CPU in interrupt context.

### Issue Context
The ISR currently retries until `write_ptr_offset` changes. In failure modes (PRU halted, missed update, misconfigured mapping) this never terminates.

### Fix Focus Areas
- Avoid waiting inside the ISR; instead, read once and compute if data progressed, or defer work to a task.
- If waiting is necessary, add a bounded retry/timeout and record an error condition.

### Fix Focus Areas (code references)
- examples/pru_eqep/mcuplus/pru_eqep_example.c[389-420]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Jul 3, 2026

Copy link
Copy Markdown

PR Summary by Qodo

am243x: add PRU-based eQEP example (AM243x-LP, ICSSG0)

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add a PRU eQEP (ABZ) example for AM243x-LP using ICSSG0 PRU/RTU/TX cores.
• Provide TI-PRU-CGT firmware projects plus a FreeRTOS R5F host app to load/run firmware.
• Document build/run flows for CCS and make-based builds.
Diagram

graph TD
  A["Build system (make/CCS)"] --> B["PRU FW headers (.h)"] --> C["R5F FreeRTOS app"] --> D["PRUICSS driver"] --> E["ICSSG0 cores (6)"] --> F[("PRU DMEM buffers")]
  E --> G["PRU INTC events"] --> C
  C --> H["UART/Debug log"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Consolidate per-core PRU build artifacts
  • ➕ Reduces duplication across six near-identical makefiles/projectspecs/linker.cmd files
  • ➕ Lowers maintenance burden when updating toolchain flags or memory maps
  • ➖ Harder to import/use in CCS where separate projects per core are often expected
  • ➖ May obscure core-specific memory maps and post-build header generation
2. Single firmware with compile-time core selection
  • ➕ One source project; core differences handled via defines
  • ➕ Potentially simpler CI/build matrix
  • ➖ Still needs distinct linker/memory placement per core type (PRU vs RTU vs TX)
  • ➖ Less explicit mapping between channel ↔ core ↔ GPI byte usage

Recommendation: Keep the current approach (separate CCS/make projects per PRU/RTU/TX core) since it matches typical ICSSG workflows and makes core-specific memory maps explicit. Consider a follow-up refactor to factor shared makefile logic (common includes/flags/post-build header naming) into a shared include to reduce repetition without changing the project import model.

Files changed (37) +3460 / -0

Enhancement (5) +1183 / -0
memory.incAdd shared PRU assembly include for channel/core mapping +167/-0

Add shared PRU assembly include for channel/core mapping

• Defines register aliases, constants, and core-specific configuration (GPI mapping, intr masks, DMEM base/offsets) for PRU/RTU/TX firmware builds.

examples/pru_eqep/firmware/include/memory.inc

main.asmAdd PRU firmware for ABZ edge capture and position update +188/-0

Add PRU firmware for ABZ edge capture and position update

• Implements a polling-based high-speed capture loop that timestamps A/B/Z transitions using the cycle counter, writes timestamps to DMEM, updates QPOS via a lookup table, and generates host interrupts when thresholds are reached.

examples/pru_eqep/firmware/main.asm

main.cAdd FreeRTOS main entry and task bootstrap +84/-0

Add FreeRTOS main entry and task bootstrap

• Provides a standard MCU+ FreeRTOS startup that initializes System/Board and runs the pru_eqep example logic inside a highest-priority task.

examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/main.c

eqep_diagnostic.hAdd ABZ diagnostic data structures and state enum +139/-0

Add ABZ diagnostic data structures and state enum

• Introduces ABZ_Handle/ABZ_Config types plus a state enum used by the host example to track timestamps, speed, direction, and position per channel.

examples/pru_eqep/mcuplus/eqep_diagnostic.h

pru_eqep_example.cAdd host-side PRUICSS firmware loader and ABZ processing +605/-0

Add host-side PRUICSS firmware loader and ABZ processing

• Implements the R5F-side initialization and runtime: loads firmware into PRU/RTU/TX cores for both slices, configures load-share and constant tables, installs 6 ISRs (events 16–21), reads DMEM ring buffers for timestamps/position, computes speed/direction, and logs results periodically.

examples/pru_eqep/mcuplus/pru_eqep_example.c

Documentation (1) +39 / -0
readme.mdDocument PRU eQEP example usage and build flows +39/-0

Document PRU eQEP example usage and build flows

• Adds README describing the PRU-based eQEP emulation, supported combinations, and step-by-step CCS and makefile build/run instructions.

examples/pru_eqep/readme.md

Other (31) +2238 / -0
example.projectspecAdd CCS projectspec for ICSSG0 PRU0 firmware +82/-0

Add CCS projectspec for ICSSG0 PRU0 firmware

• Defines a TI-PRU-CGT CCS project for AM243x-LP ICSSG0 PRU0, including include paths, core/device selection, and a post-build hexpru step that emits a PRU0 firmware header array.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/example.projectspec

linker.cmdAdd PRU0 linker command file for AM243x ICSSG0 +52/-0

Add PRU0 linker command file for AM243x ICSSG0

• Introduces PRU0 memory map/section placement for PRU IMEM/DMEM and shared RAM, tailored to slice0 usage and reserved regions for RTU/TX cores.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/linker.cmd

makefileAdd makefile for PRU0 firmware build and header export +49/-0

Add makefile for PRU0 firmware build and header export

• Adds a PRU firmware makefile using shared pru_rules.mak, setting output names and installing the generated pru0_load_bin.h to the example firmware directory.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/makefile

makefile_projectspecAdd CCS CLI build/export wrapper for PRU0 projectspec +16/-0

Add CCS CLI build/export wrapper for PRU0 projectspec

• Provides targets to export the CCS project from example.projectspec and to build/clean it via CCS headless Eclipse.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/makefile_projectspec

example.projectspecAdd CCS projectspec for ICSSG0 PRU1 firmware +82/-0

Add CCS projectspec for ICSSG0 PRU1 firmware

• Defines the AM243x-LP ICSSG0 PRU1 CCS project, including PRU1-specific defines and a post-build step producing pru1_load_bin.h.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/example.projectspec

linker.cmdAdd PRU1 linker command file for AM243x ICSSG0 +52/-0

Add PRU1 linker command file for AM243x ICSSG0

• Adds a PRU1 memory map and section placement reflecting slice1 DMEM usage and reserved regions for slice0 cores.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/linker.cmd

makefileAdd makefile for PRU1 firmware build and header export +49/-0

Add makefile for PRU1 firmware build and header export

• Builds PRU1 firmware via shared PRU rules and emits a PRU1 firmware header for inclusion by the host application.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile

makefile_projectspecAdd CCS CLI build/export wrapper for PRU1 projectspec +16/-0

Add CCS CLI build/export wrapper for PRU1 projectspec

• Adds headless CCS build/clean/export commands for the PRU1 firmware project.

examples/pru_eqep/firmware/am243x-lp/icss_g0_pru1_fw/ti-pru-cgt/makefile_projectspec

example.projectspecAdd CCS projectspec for ICSSG0 RTU_PRU0 firmware +82/-0

Add CCS projectspec for ICSSG0 RTU_PRU0 firmware

• Defines the AM243x-LP ICSSG0 RTU_PRU0 CCS project, setting RTU_PRU0-specific defines and generating rtupru0_load_bin.h.

examples/pru_eqep/firmware/am243x-lp/icss_g0_rtu_pru0_fw/ti-pru-cgt/example.projectspec

linker.cmdAdd RTU0 linker command file for AM243x ICSSG0 +52/-0

Add RTU0 linker command file for AM243x ICSSG0

• Introduces RTU instruction memory mapping and section placement, while coordinating data RAM splits with PRU/TX cores in slice0.

examples/pru_eqep/firmware/am243x-lp/icss_g0_rtu_pru0_fw/ti-pru-cgt/linker.cmd

makefileAdd makefile for RTU_PRU0 firmware build and header export +49/-0

Add makefile for RTU_PRU0 firmware build and header export

• Builds RTU_PRU0 firmware and installs the generated rtupru0_load_bin.h to the example firmware directory for host inclusion.

examples/pru_eqep/firmware/am243x-lp/icss_g0_rtu_pru0_fw/ti-pru-cgt/makefile

makefile_projectspecAdd CCS CLI build/export wrapper for RTU_PRU0 projectspec +16/-0

Add CCS CLI build/export wrapper for RTU_PRU0 projectspec

• Adds headless CCS build/clean/export commands for the RTU_PRU0 firmware project.

examples/pru_eqep/firmware/am243x-lp/icss_g0_rtu_pru0_fw/ti-pru-cgt/makefile_projectspec

example.projectspecAdd CCS projectspec for ICSSG0 RTU_PRU1 firmware +82/-0

Add CCS projectspec for ICSSG0 RTU_PRU1 firmware

• Defines the AM243x-LP ICSSG0 RTU_PRU1 CCS project with RTU_PRU1-specific defines and post-build header generation (rtupru1_load_bin.h).

examples/pru_eqep/firmware/am243x-lp/icss_g0_rtu_pru1_fw/ti-pru-cgt/example.projectspec

linker.cmdAdd RTU1 linker command file for AM243x ICSSG0 +52/-0

Add RTU1 linker command file for AM243x ICSSG0

• Introduces RTU1 instruction memory mapping and section placement, aligned with slice1 DMEM and reserved slice0 regions.

examples/pru_eqep/firmware/am243x-lp/icss_g0_rtu_pru1_fw/ti-pru-cgt/linker.cmd

makefileAdd makefile for RTU_PRU1 firmware build and header export +49/-0

Add makefile for RTU_PRU1 firmware build and header export

• Builds RTU_PRU1 firmware using shared PRU rules and installs the header used by the host to load RTU1 IRAM.

examples/pru_eqep/firmware/am243x-lp/icss_g0_rtu_pru1_fw/ti-pru-cgt/makefile

makefile_projectspecAdd CCS CLI build/export wrapper for RTU_PRU1 projectspec +16/-0

Add CCS CLI build/export wrapper for RTU_PRU1 projectspec

• Adds headless CCS build/clean/export commands for the RTU_PRU1 firmware project.

examples/pru_eqep/firmware/am243x-lp/icss_g0_rtu_pru1_fw/ti-pru-cgt/makefile_projectspec

example.projectspecAdd CCS projectspec for ICSSG0 TX_PRU0 firmware +82/-0

Add CCS projectspec for ICSSG0 TX_PRU0 firmware

• Defines the AM243x-LP ICSSG0 TX_PRU0 CCS project and generates txpru0_load_bin.h via hexpru for host loading.

examples/pru_eqep/firmware/am243x-lp/icss_g0_tx_pru0_fw/ti-pru-cgt/example.projectspec

linker.cmdAdd TX_PRU0 linker command file for AM243x ICSSG0 +52/-0

Add TX_PRU0 linker command file for AM243x ICSSG0

• Adds TX_PRU0 instruction memory mapping (6KB TX IRAM) and data section placement coordinated with slice0 DMEM splits.

examples/pru_eqep/firmware/am243x-lp/icss_g0_tx_pru0_fw/ti-pru-cgt/linker.cmd

makefileAdd makefile for TX_PRU0 firmware build and header export +49/-0

Add makefile for TX_PRU0 firmware build and header export

• Builds TX_PRU0 firmware and installs the generated txpru0_load_bin.h for inclusion in the host app build.

examples/pru_eqep/firmware/am243x-lp/icss_g0_tx_pru0_fw/ti-pru-cgt/makefile

makefile_projectspecAdd CCS CLI build/export wrapper for TX_PRU0 projectspec +16/-0

Add CCS CLI build/export wrapper for TX_PRU0 projectspec

• Adds headless CCS build/clean/export commands for the TX_PRU0 firmware project.

examples/pru_eqep/firmware/am243x-lp/icss_g0_tx_pru0_fw/ti-pru-cgt/makefile_projectspec

example.projectspecAdd CCS projectspec for ICSSG0 TX_PRU1 firmware +82/-0

Add CCS projectspec for ICSSG0 TX_PRU1 firmware

• Defines the AM243x-LP ICSSG0 TX_PRU1 CCS project and post-build conversion to txpru1_load_bin.h.

examples/pru_eqep/firmware/am243x-lp/icss_g0_tx_pru1_fw/ti-pru-cgt/example.projectspec

linker.cmdAdd TX_PRU1 linker command file for AM243x ICSSG0 +52/-0

Add TX_PRU1 linker command file for AM243x ICSSG0

• Adds TX_PRU1 instruction memory mapping and data section placement aligned with slice1 DMEM layout.

examples/pru_eqep/firmware/am243x-lp/icss_g0_tx_pru1_fw/ti-pru-cgt/linker.cmd

makefileAdd makefile for TX_PRU1 firmware build and header export +49/-0

Add makefile for TX_PRU1 firmware build and header export

• Builds TX_PRU1 firmware using shared PRU rules and installs the generated header used by the host loader.

examples/pru_eqep/firmware/am243x-lp/icss_g0_tx_pru1_fw/ti-pru-cgt/makefile

makefile_projectspecAdd CCS CLI build/export wrapper for TX_PRU1 projectspec +16/-0

Add CCS CLI build/export wrapper for TX_PRU1 projectspec

• Adds headless CCS build/clean/export commands for the TX_PRU1 firmware project.

examples/pru_eqep/firmware/am243x-lp/icss_g0_tx_pru1_fw/ti-pru-cgt/makefile_projectspec

makefileAdd top-level example makefile to build PRU firmware + MCU+ host +110/-0

Add top-level example makefile to build PRU firmware + MCU+ host

• Introduces the example build entrypoint with device gating, PRU-first build ordering, and AM243x-LP targets that build all six ICSSG0 core firmwares plus the R5F FreeRTOS host project.

examples/pru_eqep/makefile

example.syscfgAdd SysConfig for PRUICSS, INTC mapping, and UART logging +315/-0

Add SysConfig for PRUICSS, INTC mapping, and UART logging

• Configures PRUICSS instance settings, GPIO usage, host interrupt mappings for events 16–21, and debug UART logging for the AM243x R5F FreeRTOS application.

examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/example.syscfg

example.projectspecAdd CCS projectspec for AM243x R5F FreeRTOS host app +123/-0

Add CCS projectspec for AM243x R5F FreeRTOS host app

• Defines a CCS project using TI ARM Clang with SysConfig integration, includes PRU firmware header include paths, and runs boot image generation as a post-build step.

examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/example.projectspec

makefileAdd MCU+ host makefile (SysConfig + appimage generation) +355/-0

Add MCU+ host makefile (SysConfig + appimage generation)

• Adds an auto-generated build that compiles the FreeRTOS host sources, runs SysConfig generation, links against MCU+ libraries, and produces an SBL-loadable appimage (with optional HS signing).

examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile

makefile_ccs_bootimage_genAdd CCS post-build appimage generation script +113/-0

Add CCS post-build appimage generation script

• Provides the CCS-invoked post-build steps to convert the R5F ELF to RPRC/appimage and optionally sign images depending on device type and encryption settings.

examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_ccs_bootimage_gen

makefile_projectspecAdd CCS CLI build/export wrapper for R5F projectspec +16/-0

Add CCS CLI build/export wrapper for R5F projectspec

• Adds headless CCS build/clean/export commands for the R5F FreeRTOS host project.

examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/makefile_projectspec

syscfg_c.rov.xsAdd ROV metadata for FreeRTOS viewing +12/-0

Add ROV metadata for FreeRTOS viewing

• Adds ROV configuration to enable FreeRTOS object viewing in CCS/ROV tooling.

examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/syscfg_c.rov.xs

Comment on lines +122 to +129
sbbo &time_stamp, DMEM0, buffer_addr, 4
sbco &buffer_addr, DMEM1, WRITE_PTR_OFFSET, 2
sbco &QPOS, DMEM1, CH_POS_OFFSET, 4

; Handle buffer wrap-around
qbgt no_buffer_wrap, buffer_addr, buffer_size
ldi buffer_addr, 0
ldi READ_POS_BUFF, 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. sbbo before buffer bounds 📘 Rule violation ⛨ Security

sbbo &time_stamp, DMEM0, buffer_addr, 4 stores to DMEM using buffer_addr before verifying that
buffer_addr + 4 stays within buffer_size, and the wrap check only triggers on `buffer_addr >
buffer_size (allowing buffer_addr == buffer_size`). This can write past the intended buffer end
and corrupt adjacent PRU memory.
Agent Prompt
## Issue description
The PRU firmware writes a 4-byte timestamp to `DMEM0 + buffer_addr` before ensuring `buffer_addr` is within the valid buffer range, and the wrap condition allows `buffer_addr == buffer_size`.

## Issue Context
Compliance requires bounds checks for memory accesses, including validating `offset + size` against named limits.

## Fix Focus Areas
- examples/pru_eqep/firmware/main.asm[122-129]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +33 to +49
.asg r0, scratch
.asg r1, scratch2
.asg r17.b0, curr_gpi_sample ; Current GPI state after masking
.asg r17.b1, A_B_Z_edge ; Edge detection for A,B,Z signals
.asg r18, curr_ts ; Timestamp counter
.asg r19.b0, edge ; Edge detection
.asg r20.b0, prev_sample ; Previous state of A,B,Z signals
.asg r21, time_stamp ; Time stamp counter
.asg r22.w0, buffer_addr ; Buffer address pointer
.asg r22.w2, buffer_size ; Buffer size
.asg r23, wr_ptr ; Write pointer
.asg r24.w0, READ_POS_BUFF ; Read position buffer
.asg r24.w2, read_pos_speed_intr ; Read position speed interrupt
.asg r26.b0, a_b_transition ; A/B transition state
.asg r26.b1, qpos_update ; 2-> QPOS--, 1-> QPOS++, 0-> no change
.asg r27, QPOS ; Position counter
.asg r28, DMEM0 ; Data memory pointer

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Direct .asg register aliases 📘 Rule violation ⚙ Maintainability

The new PRU assembly include uses extensive direct register aliasing via .asg instead of
.struct/.sassign. This increases risk of register overlap/corruption and makes calling/register
conventions harder to audit.
Agent Prompt
## Issue description
The PRU assembly register map is implemented with many `.asg` aliases, contrary to the preferred `.struct`/`.sassign` pattern.

## Issue Context
The compliance rule requires structured register allocation to reduce overlap/corruption risk and improve maintainability.

## Fix Focus Areas
- examples/pru_eqep/firmware/include/memory.inc[33-49]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +55 to +57
postBuildStep="
$(CG_TOOL_ROOT)/bin/hexpru --diag_wrap=off --array --array:name_prefix=PRU0Firmware -o ../pru0_load_bin.h ${BuildArtifactFileBaseName}.out;
"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Long postbuildstep command line 📘 Rule violation ⚙ Maintainability

New .projectspec files embed long command strings in a single line (for example,
postBuildStep=...), which harms readability and violates the repository’s configuration
line-length expectations. This makes future edits and reviews error-prone.
Agent Prompt
## Issue description
Project configuration includes very long single-line command attributes (notably `postBuildStep`), reducing maintainability and violating line-length guidance.

## Issue Context
The compliance rule requests wrapping/reflowing long command-like attributes where feasible.

## Fix Focus Areas
- examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/example.projectspec[55-57]
- examples/pru_eqep/mcuplus/am243x-lp/r5fss0-0_freertos/ti-arm-clang/example.projectspec[70-70]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +20 to +23
- **When using CCS projects to build**, import the CCS project from the above mentioned Example folder path for R5F and PRU, After this `main.asm`, `linker.cmd` files gets copied to ccs workspace of PRU project. The `main.asm` contains sample code to halt PRU program

- Build the PRU project using the CCS project menu (see [for AM64x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/latest/exports/docs/api_guide_am64x/CCS_PROJECTS_PAGE.html), [for AM243x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM243X/latest/exports/docs/api_guide_am243x/CCS_PROJECTS_PAGE.html), [for AM261x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM261X/latest/exports/docs/api_guide_am261x/CCS_PROJECTS_PAGE.html), [for AM263x](https://software-dl.ti.com/mcu-plus-sdk/esd/AM263X/latest/exports/docs/api_guide_am263x/CCS_PROJECTS_PAGE.html), [for AM263Px](https://software-dl.ti.com/mcu-plus-sdk/esd/AM263PX/latest/exports/docs/api_guide_am263px/CCS_PROJECTS_PAGE.html)).
- Build Flow: Once you click on build in PRU project, firmware header file which is generated in release or debug folder of ccs workspace, is moved to `<open-pru/examples/empty/firmware/device/>`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. readme.md wrong empty path 📘 Rule violation ≡ Correctness

The README references <open-pru/examples/empty/firmware/device/>, which does not match this
example’s actual location and can mislead users. The same section also contains extremely long
markdown lines, reducing portability/readability in rendered views and diffs.
Agent Prompt
## Issue description
The new README contains an incorrect repository path reference (points to `examples/empty` instead of `examples/pru_eqep`) and includes very long single-line markdown content.

## Issue Context
Incorrect path references break portability/usability of docs, and overly long markdown lines violate documentation formatting expectations.

## Fix Focus Areas
- examples/pru_eqep/readme.md[20-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +31 to +34
# pru_rules.mak has shared settings for all PRU/RTU/TX_PRU core makefiles
include $(OPEN_PRU_PATH)/pru_rules.mak
include $(OPEN_PRU_PATH)/examples/pru_eqep/firmware/include/memory.inc
# Optional:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

5. Asm file included by make 🐞 Bug ≡ Correctness

The PRU0 firmware makefile uses GNU make include to include memory.inc, but that file contains
assembly syntax (e.g., ; comments and .asg) and will be parsed as make syntax, causing the build
to fail.
Agent Prompt
### Issue description
`examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/makefile` includes an assembly `.inc` file via make's `include`, which will break parsing and stop the build.

### Issue Context
`memory.inc` is meant to be included by the PRU assembler (via `.include` in `main.asm` or via compiler include paths), not by `make`.

### Fix Focus Areas
- Remove the `include $(OPEN_PRU_PATH)/examples/pru_eqep/firmware/include/memory.inc` line.
- Ensure the assembler can still find `memory.inc` via `INCLUDE += --include_path=...`.

### Fix Focus Areas (code references)
- examples/pru_eqep/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/makefile[31-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread examples/pru_eqep/firmware/main.asm
Comment on lines +569 to +583
void ABZ_enable_load_share_mode(void *pruCfg, uint32_t pruSlice)
{
uint32_t regVal;
if(pruSlice == 1)
{
regVal = HW_RD_REG32((uint8_t *)pruCfg + CSL_ICSSCFG_EDPRU1TXCFGREGISTER_PRU1_ED_TX_CLK_SEL_MASK);
regVal |= CSL_ICSSCFG_EDPRU1TXCFGREGISTER_PRU1_ENDAT_SHARE_EN_MASK;
HW_WR_REG32((uint8_t *)pruCfg + CSL_ICSSCFG_EDPRU1TXCFGREGISTER, regVal);
}
else
{
regVal = HW_RD_REG32((uint8_t *)pruCfg + CSL_ICSSCFG_EDPRU0TXCFGREGISTER_PRU0_ED_TX_CLK_SEL_MASK);
regVal |= CSL_ICSSCFG_EDPRU0TXCFGREGISTER_PRU0_ENDAT_SHARE_EN_MASK;
HW_WR_REG32((uint8_t *)pruCfg + CSL_ICSSCFG_EDPRU0TXCFGREGISTER, regVal);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

8. Mask used as register offset 🐞 Bug ≡ Correctness

ABZ_enable_load_share_mode() adds *_MASK constants to the PRU CFG base pointer when reading the
EDPRU TX CFG register, but those constants are bitmasks (e.g., 0x10) rather than register offsets
(e.g., 0xE4/0x104), so it reads the wrong address and may misconfigure load-share mode.
Agent Prompt
### Issue description
The code reads a PRU CFG register using a bitmask macro as if it were a register offset, so the value read is from the wrong location.

### Issue Context
`CSL_ICSSCFG_EDPRU1TXCFGREGISTER_PRU1_ED_TX_CLK_SEL_MASK` is 0x10 (mask), while `CSL_ICSSCFG_EDPRU1TXCFGREGISTER` is 0x104 (offset). The same pattern exists for PRU0.

### Fix Focus Areas
- Replace the `HW_RD_REG32(base + *_MASK)` reads with `HW_RD_REG32(base + CSL_ICSSCFG_EDPRU{0,1}TXCFGREGISTER)`.
- Keep using the `*_ENDAT_SHARE_EN_MASK` bitmask when setting/clearing bits in the read value.

### Fix Focus Areas (code references)
- examples/pru_eqep/mcuplus/pru_eqep_example.c[569-583]
- source/include/am243x/cslr_icss_g.h[13069-13071]
- source/include/am243x/cslr_icss_g.h[14785-14789]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +317 to +322
ABZHandle[0]->position_base = (void *)((uint32_t)(ABZHandle[0]->baseMemAddr1) + CH0_POSITION_OFFSET);
ABZHandle[1]->position_base = (void *)((uint32_t)(ABZHandle[1]->baseMemAddr1) + CH0_POSITION_OFFSET);
ABZHandle[2]->position_base = (void *)((uint32_t)(ABZHandle[2]->baseMemAddr1) + CH0_POSITION_OFFSET);
ABZHandle[3]->position_base = (void *)((uint32_t)(ABZHandle[3]->baseMemAddr1) + CH0_POSITION_OFFSET);
ABZHandle[4]->position_base = (void *)((uint32_t)(ABZHandle[4]->baseMemAddr1) + CH0_POSITION_OFFSET);
ABZHandle[5]->position_base = (void *)((uint32_t)(ABZHandle[5]->baseMemAddr1) + CH0_POSITION_OFFSET);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

9. Position base uses ch0 offset 🐞 Bug ≡ Correctness

All six channels set position_base using CH0_POSITION_OFFSET, ignoring the distinct offsets
defined for channels 1-5, so channels 1-5 will read the wrong position value and report incorrect
direction/position.
Agent Prompt
### Issue description
`position_base` is initialized with the same offset for every channel, even though each channel has a different position offset.

### Issue Context
The file defines `CH1_POSITION_OFFSET` ... `CH5_POSITION_OFFSET`, but they are not used when setting `ABZHandle[i]->position_base`.

### Fix Focus Areas
- Set `position_base` for each channel using the matching `CHx_POSITION_OFFSET`.
- Optionally use a small lookup array to avoid repetitive code.

### Fix Focus Areas (code references)
- examples/pru_eqep/mcuplus/pru_eqep_example.c[67-73]
- examples/pru_eqep/mcuplus/pru_eqep_example.c[317-322]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +309 to +314
ABZHandle[i]->QPOSCOUNT = 0;
ABZHandle[i]->read_ptr = ABZHandle[i]->baseMemAddr0;
ABZHandle[i]->write_ptr = ABZHandle[i]->baseMemAddr1;
ABZHandle[i]->mem_limit=(DMEM_CHANNEL_SIZE)*(i) + DMEM_CHANNEL_SIZE;
if(i<3) ABZHandle[i]->pru_slice=0;
else ABZHandle[i]->pru_slice=1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

10. Mem_limit scaled by channel 🐞 Bug ≡ Correctness

mem_limit is computed as DMEM_CHANNEL_SIZE*i + DMEM_CHANNEL_SIZE, but the PRU firmware write
pointer (buffer_addr) is a per-channel offset that wraps to 0; for channels >0 this inflates
wrap-around edge counts and produces incorrect speed.
Agent Prompt
### Issue description
Speed calculation uses `mem_limit` as the buffer end when the write pointer wraps, but `mem_limit` is incorrectly made larger for higher channel indices.

### Issue Context
`EQEP_Get_Speed_ABZ()` uses `mem_limit` only as an upper bound for `(mem_limit - read_ptr_offset + write_ptr_offset)` when `read_ptr_offset > write_ptr_offset` (wrap case). Since firmware resets `buffer_addr` back to 0 on wrap, `mem_limit` must be the fixed per-channel buffer size.

### Fix Focus Areas
- Change `ABZHandle[i]->mem_limit` to `DMEM_CHANNEL_SIZE` (or the correct per-channel buffer size constant), not scaled by `i`.
- Ensure the host-side buffer size constant matches the firmware’s `MAX_BUFFER`/wrap behavior.

### Fix Focus Areas (code references)
- examples/pru_eqep/mcuplus/pru_eqep_example.c[294-316]
- examples/pru_eqep/mcuplus/pru_eqep_example.c[485-510]
- examples/pru_eqep/firmware/main.asm[126-130]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +394 to +401
if (!(ABZHandle[0]->write_ptr_offset == (HW_RD_REG32(ABZHandle[0]->baseMemAddr1) & WRITE_PTR_OFFSET_MASK)))
{
EQEP_Get_Speed_ABZ(0);
}
else
{
while (ABZHandle[0]->write_ptr_offset == (HW_RD_REG32(ABZHandle[0]->baseMemAddr1) & WRITE_PTR_OFFSET_MASK));
EQEP_Get_Speed_ABZ(0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

11. Spin loop inside isr 🐞 Bug ☼ Reliability

Each PRU host interrupt ISR busy-waits in a tight while (...) ; loop until the PRU updates the
write pointer; if the PRU stalls or the pointer never changes, the ISR can run indefinitely and
block other interrupts/tasks.
Agent Prompt
### Issue description
The interrupt handlers contain an unbounded spin-wait, which can lock the CPU in interrupt context.

### Issue Context
The ISR currently retries until `write_ptr_offset` changes. In failure modes (PRU halted, missed update, misconfigured mapping) this never terminates.

### Fix Focus Areas
- Avoid waiting inside the ISR; instead, read once and compute if data progressed, or defer work to a task.
- If waiting is necessary, add a bounded retry/timeout and record an error condition.

### Fix Focus Areas (code references)
- examples/pru_eqep/mcuplus/pru_eqep_example.c[389-420]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@pratheesh

Copy link
Copy Markdown
Contributor

Took a detailed pass through this PR, verifying each item against the code, the CSL headers, and CI. Grouping the Qodo findings by whether they hold up, plus a couple of independent items.

Confirmed — worth fixing (+1)

  • am243x: pru io: Add pru emif example & firmware #5 (make include memory.inc) — this is what makes Build (am243x) red. firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/makefile:33 does include $(OPEN_PRU_PATH)/examples/pru_eqep/firmware/include/memory.inc, but memory.inc is PRU assembly (.asg, ;-comments, .if $isdefed). GNU make tries to parse it and errors out. The assembler already gets it via the include path added on line 38 (INCLUDE += --include_path=.../include), so line 33 is both redundant and build-breaking — just remove it. (The other SoCs pass only because pru_eqep is am243x-only.)

  • Am263x: PRU-I2S #8 (mask used as register offset) — real read-modify-write bug. In ABZ_enable_load_share_mode(), the read uses the mask as the address offset while the write uses the register offset:

    • CSL_ICSSCFG_EDPRU1TXCFGREGISTER = 0x104, but ..._PRU1_ED_TX_CLK_SEL_MASK = 0x10
    • CSL_ICSSCFG_EDPRU0TXCFGREGISTER = 0xE4, but ..._PRU0_ED_TX_CLK_SEL_MASK = 0x10
      So it reads from the wrong register (base+0x10), ORs in the enable bit, and writes the result to the correct register. The read offset should be the ...REGISTER, not the ...MASK.
  • A0503545 add sdk infra to am243x am64x #1 (write past buffer end) — real off-by-one. sbbo &time_stamp, DMEM0, buffer_addr, 4 writes before the wrap check, and buffer_addr is incremented all the way up to buffer_size (MAX_BUFFER = 0x400) before it is reset — so one store lands at offset 0x400, i.e. 4 bytes into the next channel's region (channels are 0x400 apart per DMEM_CHx_OFFSET). Wrap should trigger at buffer_addr >= buffer_size - 4, or bounds-check before the store.

  • Added AM26x support for Open PRU #9 (position_base) — CH1–CH5 offsets are dead code. Lines 317–322 set position_base with CH0_POSITION_OFFSET for all six channels; CH1_POSITION_OFFSET … CH5_POSITION_OFFSET are defined but never used. Either wire each channel to its own offset or drop the unused defines.

  • A0503595 spi loopback example #6 (cur_sample byte) — the per-core .if/.elseif is currently a no-op. All three branches assign r31.b0, while the comments (and the "GPI Byte Selection Guide") say b0 / b1 / b2 for RTU / PRU / TX_PRU. As written the conditional does nothing. If the PRU/TX_PRU cores really sample GPIs 8–15 / 16–19 this is a functional bug; if not, the branches and the guide are misleading and should be trimmed.

Disagree — please do not apply this one

  • am64x/am243x : infra : add scripts to install node modules #7 (buffer wrap reversed) — false positive; the current code is correct. In TI PRU assembly QBGT label, a, b branches when b > a (cf. this repo's own qbgt END_FOR_LOOP, N, I0 ; loop till I0>N). So qbgt no_buffer_wrap, buffer_addr, buffer_size takes the increment path while buffer_addr < buffer_size and falls through to the reset otherwise — which is the intended behaviour. Reversing it as the bot suggests would break a working loop. The only real issue in this block is the off-by-one already covered in A0503545 add sdk infra to am243x am64x #1.

Low priority / subjective

Independent items (not flagged by the bot)

  • Firmware↔C position offset mismatch. The firmware writes the position at DMEM1 + CH_POS_OFFSET (0x30), but the C side reads it at baseMemAddr1 + CH0_POSITION_OFFSET (0x1C). Even after Added AM26x support for Open PRU #9, 0x1C != 0x30, so the R5F would read position from the wrong location — please reconcile the shared-memory layout between main.asm and pru_eqep_example.c.
  • MAX_BUFFER .set 0x400 ; Buffer size = 8 kB0x400 is 1 KB, not 8 KB. Fix the comment (and confirm the intended size).

Happy to re-verify once these are addressed — particularly a green Build (am243x) after #5.

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@a1248924

a1248924 commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator Author

thanks @pratheesh for grouping the comments, will review it carefully

@a1248924 a1248924 force-pushed the a1248924_EQEP branch 2 times, most recently from 54bfcd6 to 82a3d5f Compare July 5, 2026 19:20
Signed-off-by: Ayushman a-ayushman@ti.com
if (ABZHandle[channel]->prev_ts > ABZHandle[channel]->cur_ts)
ABZHandle[channel]->delta_t = TIMESTAMP_MASK - ABZHandle[channel]->prev_ts + ABZHandle[channel]->cur_ts;
else
ABZHandle[channel]->delta_t = ABZHandle[channel]->cur_ts - ABZHandle[channel]->prev_ts;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

construct a case where delta_t becomes zero leading to division by zero, for timing calculations, only 24 bits are being considered currently

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants