Skip to content

Fix issue #120: Add HW spinlock usage examples (assembly/C) in academy/spinlock#142

Open
pratheesh wants to merge 2 commits into
TexasInstruments:mainfrom
pratheesh:fix/issue-120-spinlock-examples
Open

Fix issue #120: Add HW spinlock usage examples (assembly/C) in academy/spinlock#142
pratheesh wants to merge 2 commits into
TexasInstruments:mainfrom
pratheesh:fix/issue-120-spinlock-examples

Conversation

@pratheesh

@pratheesh pratheesh commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Closes #120.

Adds HW spinlock usage examples (assembly and C) under academy/spinlock/.

Approach

Uses the PRU_ICSSG local hardware spinlock accessed over the broadside interface (XIN/XOUT), per the recommendation in #120. The spinlock accelerator owns 64 ownership flags (lock id 0–63), reached with broadside device id 0x90 (= 144) for the local PRU subsystem:

  • Lock id (0–63) is placed in R1.b0.
  • Acquire: XIN 0x90, &R1.b3, 1 → acquisition status in R1.b3 bit 0 (1 = acquired). One XIN is one attempt; busy-wait until acquired.
  • Release: XOUT 0x90, &R1.b3, 1 with the same lock id still in R1.b0.

A spinlock provides mutual exclusion only (not a mailbox); the README covers the correct signalling pattern (guard a shared buffer + raise a system event).

Files

  • spinlock.asm — C-callable spinlock_acquire() / spinlock_release() built on the broadside primitives.
  • main.c — C example: PRU0 and PRU1 contend for one lock and drive a per-core debug GPO while holding it, so ownership is observable on a scope.
  • spinlock_example.asm — pure-assembly example using M_SPINLOCK_ACQUIRE / M_SPINLOCK_RELEASE macros.
  • readme.md — semantics, usage pattern, and build notes.

Validation

All sources assemble/compile with clpru --silicon_version=3 (ti-cgt-pru 2.3.3); main.c builds for both PRU0 (-DPRU0) and PRU1.

Notes

Squashed to a single commit. An earlier revision of this PR used the SoC-level memory-mapped Spinlock module (0x2A000000); it has been reworked to the broadside local-spinlock approach requested in the issue.

@qodo-code-review

qodo-code-review Bot commented Jun 13, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (7) 📎 Requirement gaps (2)

Context used

Grey Divider


Action required

1. Busy-wait worst-case undocumented 📎 Requirement gap ☼ Reliability ⭐ New
Description
The README describes spinning until acquisition succeeds but does not explicitly document that the
busy-wait can be unbounded in the worst case, as required. This can mislead users about
timing/determinism implications of the blocking acquire loop.
Code

academy/spinlock/readme.md[R8-21]

+The PRU_ICSSG includes a hardware spinlock accelerator with **64 ownership flags**
+(lock id `0`–`63`). A PRU core reaches it over the **broadside interface** using the
+`XIN`/`XOUT` instructions — there is no memory-mapped load/store on the critical path,
+so acquire/release are only a couple of cycles.
+
+- The broadside device id (XID) **`0x90` (= 144)** selects the spinlock in the
+  **local** PRU subsystem. A different XID is needed to reach a spinlock in another
+  PRU subsystem.
+- The **lock id** to operate on is placed in **`R1.b0`**.
+- **Acquire** (`XIN 0x90, &R1.b3, 1`): the accelerator returns the acquisition status
+  in **`R1.b3`** — **bit 0 = 1** means the lock is now owned by this core, **bit 0 = 0**
+  means it is held by someone else, so retry. A single `XIN` is one attempt; busy-wait
+  until you get a 1.
+- **Release** (`XOUT 0x90, &R1.b3, 1`): with the same lock id still in `R1.b0`, frees
Evidence
PR Compliance ID 3 requires explicit documentation of best/worst-case timing, including that the
busy-wait acquire has unbounded worst-case time. The README instructs to busy-wait until success but
does not explicitly state the worst-case is unbounded.

Document the blocking busy-wait semantics and status interpretation for acquisition
academy/spinlock/readme.md[8-21]

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 documentation explains the status bit and that the PRU should busy-wait, but it does not explicitly state the worst-case acquisition time is unbounded.

## Issue Context
PR Compliance requires documenting busy-wait semantics including best/worst-case timing characteristics and how acquisition success is detected.

## Fix Focus Areas
- academy/spinlock/readme.md[8-22]

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


2. spinlock.asm missing required metadata 📘 Rule violation ⚙ Maintainability ⭐ New
Description
The new assembly functions spinlock_acquire/spinlock_release are added without the mandatory
function metadata (pseudo code, peak/worst-case cycles, and registers modified). This reduces
reviewability and can hide timing/register-clobber assumptions in a low-level synchronization
primitive.
Code

academy/spinlock/spinlock.asm[R35-66]

+;******************************************************************************
+; uint8_t spinlock_acquire(uint8_t flag_id);
+;
+;   One non-blocking attempt to acquire spinlock 'flag_id'.
+;   Returns 1 if the lock was acquired, 0 if it is held by someone else.
+;
+;   Calling convention (PRU C/C++ compiler): the uint8_t argument arrives in
+;   R14.b0 and the uint8_t return value is passed back in R14.b0. The return
+;   address is in r3.w2.
+;******************************************************************************
+    .sect       ".text:spinlock_acquire"
+    .clink
+    .global     spinlock_acquire
+spinlock_acquire:
+    MOV         R1.b0, R14.b0           ; lock id -> R1.b0 (presented to accel)
+    XIN         INT_SPIN_XID, &R1.b3, 1 ; status returned in R1.b3 (bit0=acquired)
+    MOV         R14.b0, R1.b3           ; return status
+    JMP         r3.w2
+
+;******************************************************************************
+; void spinlock_release(uint8_t flag_id);
+;
+;   Release spinlock 'flag_id'. Only the owner should release, and it must do
+;   so promptly.
+;******************************************************************************
+    .sect       ".text:spinlock_release"
+    .clink
+    .global     spinlock_release
+spinlock_release:
+    MOV         R1.b0, R14.b0           ; lock id -> R1.b0
+    XOUT        INT_SPIN_XID, &R1.b3, 1 ; release the lock
+    JMP         r3.w2
Evidence
PR Compliance ID 24 requires pseudo code, peak/worst-case cycle budget, and registers modified
documentation for PRU assembly functions/macros. The function header comments for spinlock_acquire
and spinlock_release do not include these required fields.

academy/spinlock/spinlock.asm[35-66]
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 assembly functions added in `spinlock.asm` do not include the required metadata fields: pseudo code, peak/worst-case cycle count, and registers modified.

## Issue Context
The compliance checklist requires standardized documentation for PRU assembly functions/macros to prevent timing and register-corruption bugs.

## Fix Focus Areas
- academy/spinlock/spinlock.asm[35-66]

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


3. main.c header missing filename 📘 Rule violation ⚙ Maintainability
Description
academy/spinlock/main.c has a copyright/SPDX header but does not denote the file name in the
header section. This violates the required source file header convention and reduces traceability.
Code

academy/spinlock/main.c[R1-16]

+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C) 2024-2026 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Spinlock Example - C Version (PRU_ICSSG local hardware spinlock)
+ *
+ * Demonstrates mutual exclusion between PRU0 and PRU1 using the PRU-local
+ * hardware spinlock, accessed over the broadside interface (XIN/XOUT). The
+ * low-level acquire/release are implemented in spinlock.asm; this file shows
+ * the usage pattern.
+ *
+ * Each PRU repeatedly takes the same spinlock, drives a debug GPO high while it
+ * holds the lock, waits a (per-core different) number of cycles, then releases
+ * and drives the debug GPO low. Probing the two debug pins shows that the two
+ * cores never hold the lock at the same time.
+ */
Evidence
PR Compliance ID 18 requires a file header that includes the file name and TI copyright statement.
The added header in main.c includes SPDX and copyright but does not include a File:/file-name
line identifying main.c.

academy/spinlock/main.c[1-16]
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
`academy/spinlock/main.c` is missing the required header field that denotes the file name.
## Issue Context
The compliance checklist requires each source file header to include both the file name and the TI copyright statement.
## Fix Focus Areas
- academy/spinlock/main.c[1-16]

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


View more (10)
4. TODO marker in main.c 📘 Rule violation ⚙ Maintainability
Description
academy/spinlock/main.c introduces a TODO marker, which is disallowed by the repository marker
standard. This makes tracking inconsistent and violates the required pending-work marker convention.
Code

academy/spinlock/main.c[R35-36]

+/* TODO: pinmux the debug signals (see the GPIO lab) and update the shift to */
+/*       match your board. PRU0 is selected via -DPRU0 in the makefile.      */
Evidence
PR Compliance ID 23 forbids introducing TODO markers. The comment in main.c contains TODO:
indicating pending work.

academy/spinlock/main.c[35-36]
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 code introduces a `TODO` marker; the repository standard requires `FIXME` instead.
## Issue Context
Use `FIXME` for pending work markers to keep consistency across the codebase.
## Fix Focus Areas
- academy/spinlock/main.c[35-36]

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


5. Multiple spaces in main.c 📘 Rule violation ⚙ Maintainability
Description
academy/spinlock/main.c contains multiple consecutive spaces within declarations for alignment
(for example between void and spinlock_release). This violates the whitespace policy that
restricts whitespace to single spaces and newlines.
Code

academy/spinlock/main.c[R28-29]

+uint8_t spinlock_acquire(uint8_t flag_id);
+void    spinlock_release(uint8_t flag_id);
Evidence
PR Compliance ID 16 disallows tabs and multiple spaces outside the documented Markdown exception.
The declaration void    spinlock_release(...) contains multiple consecutive spaces in the changed
C source.

academy/spinlock/main.c[28-29]
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 file uses multiple consecutive spaces for alignment, but the whitespace policy requires single spaces only.
## Issue Context
This applies to changed content in source files; alignment should be done without introducing multiple spaces (use one space and rely on formatting tools/settings if needed).
## Fix Focus Areas
- academy/spinlock/main.c[28-29]

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


6. Macros missing required docs 📘 Rule violation ⚙ Maintainability
Description
The new PRU assembly macros M_SPINLOCK_ACQUIRE and M_SPINLOCK_RELEASE are documented but do not
include all mandatory fields (pseudo code and registers modified). This reduces reviewability and
can hide register-clobber and timing assumptions.
Code

academy/spinlock/spinlock_example.asm[R29-51]

+;******************************************************************************
+;   Macro: M_SPINLOCK_ACQUIRE
+;       Acquire the spinlock and busy-wait until successful.
+;       R1.b0 - Input : spinlock id (set to SPINLOCK_ID)
+;       R1.b3 - Output: acquisition status (bit 0: 0=failed, 1=acquired)
+;       Best case 3 cycles; worst case unbounded (busy-wait).
+;******************************************************************************
+M_SPINLOCK_ACQUIRE  .macro
+    .newblock
+    LDI     R1.b0, SPINLOCK_ID          ; lock id (0-63)
+$1:
+    XIN     INT_SPIN_XID, &R1.b3, 1     ; attempt acquire; status in R1.b3
+    QBBC    $1, R1.b3, 0                ; bit 0 clear -> not acquired, retry
+    .endm
+
+;******************************************************************************
+;   Macro: M_SPINLOCK_RELEASE
+;       Release the spinlock. Must follow M_SPINLOCK_ACQUIRE (lock id is still
+;       in R1.b0). 2 cycles.
+;******************************************************************************
+M_SPINLOCK_RELEASE  .macro
+    XOUT    INT_SPIN_XID, &R1.b3, 1     ; release lock held in R1.b0
+    .endm
Evidence
PR Compliance ID 29 requires pseudo code, peak cycle budget, and registers modified for PRU assembly
functions/macros. The comment blocks for M_SPINLOCK_ACQUIRE/M_SPINLOCK_RELEASE describe behavior
and some timing notes but do not list pseudo code and the registers modified (for example
R1.b0/R1.b3).

academy/spinlock/spinlock_example.asm[29-51]
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
PRU assembly macro documentation is missing mandatory fields (pseudo code, registers modified).
## Issue Context
The compliance checklist requires PRU macros/functions to document pseudo code, peak/worst-case cycle budget, and registers modified.
## Fix Focus Areas
- academy/spinlock/spinlock_example.asm[29-51]

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


7. Acquire return not boolean 🐞 Bug ≡ Correctness
Description
spinlock_acquire() claims to return 0/1 but actually returns the raw R1.b3 status byte; main.c
spins on result != 1, so it can keep spinning even when bit0 indicates the lock is acquired (if
any other status bits are set). This makes the example’s acquire loop depend on an exact byte value
instead of the documented “bit0=success” contract.
Code

academy/spinlock/spinlock.asm[R49-52]

+    MOV         R1.b0, R14.b0           ; lock id -> R1.b0 (presented to accel)
+    XIN         INT_SPIN_XID, &R1.b3, 1 ; status returned in R1.b3 (bit0=acquired)
+    MOV         R14.b0, R1.b3           ; return status
+    JMP         r3.w2
Evidence
The helper’s comment promises a boolean return, but the implementation returns the full status byte.
The documentation and assembly macro examples only test bit 0, while main.c requires the return to
equal 1 exactly, creating a contract mismatch.

academy/spinlock/spinlock.asm[36-52]
academy/spinlock/main.c[58-63]
academy/spinlock/readme.md[17-21]
academy/spinlock/spinlock_example.asm[36-42]

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

## Issue description
`spinlock_acquire()` is documented as returning `1` when acquired and `0` otherwise, but it currently returns the entire status byte from `R1.b3`. The C example (`main.c`) and README usage pattern compare the return value to `1`, so the loop is only correct if the hardware always returns exactly `0x00` or `0x01`.
## Issue Context
The README and the pure-assembly macro example both treat only **bit 0** as meaningful (they test the bit), not the entire byte.
## Fix Focus Areas
- academy/spinlock/spinlock.asm[48-52]
- academy/spinlock/main.c[58-63]
## Suggested fix
1. Normalize the return value in `spinlock_acquire()` to `0`/`1` by masking `R1.b3` to bit0 before moving into `R14.b0` (e.g., `AND` with `0x01`).
2. (Optional hardening) Update the C loop to test bit0 (`(spinlock_acquire(...) & 1) != 0`) so the usage pattern matches the documented semantics even if the helper is reused elsewhere.

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


8. Busy-wait loop lacks bound 📘 Rule violation ☼ Reliability
Description
The assembly acquire loop can spin forever because it has no timeout or max-iteration termination
condition. This can cause non-deterministic firmware behavior and a hard hang if the lock is never
released.
Code

academy/spinlock/spinlock_example.asm[R59-65]

+acquire_lock0:
+    ; Atomic test-and-set: a read of the lock register attempts to take it.
+    lbbo    &LOCK_VAL, SPINLOCK_BASE_REG, LOCK0_OFFSET, 4
+    ; read != 0  -> lock was already held, keep spinning.
+    ; read == 0  -> lock is now OWNED by this PRU, fall through.
+    qbne    acquire_lock0, LOCK_VAL, 0
+
Evidence
PR Compliance ID 5 requires loops in PRU assembly to have explicit termination conditions. The new
acquire_lock0 loop (qbne acquire_lock0, LOCK_VAL, 0) has no counter/timeout exit path, so it can
spin indefinitely.

academy/spinlock/spinlock_example.asm[59-65]
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 busy-wait acquire loop has no explicit termination bound, so it can run indefinitely.
## Issue Context
Compliance requires deterministic loops with termination conditions/bounds checking in PRU assembly.
## Fix Focus Areas
- academy/spinlock/spinlock_example.asm[59-65]

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


9. Uses .asg register aliases 📘 Rule violation ⚙ Maintainability
Description
The assembly example allocates registers via .asg aliases instead of using .struct/.sassign.
This increases the risk of register overlap/corruption and violates the required structured register
mapping convention.
Code

academy/spinlock/spinlock_example.asm[R37-40]

+; definitions
+    .asg    R2,    SPINLOCK_BASE_REG   ; holds the Spinlock module base address
+    .asg    R3,    LOCK_VAL            ; scratch for the value read from / written to the lock
+
Evidence
PR Compliance ID 8 requires .struct/.sassign for register allocation. The new example uses `.asg
R2 and .asg R3 to create aliases (SPINLOCK_BASE_REG, LOCK_VAL`) instead of structured
assignments.

academy/spinlock/spinlock_example.asm[37-40]
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
PRU assembly code must map registers using `.struct` and `.sassign`, rather than `.asg` aliases to raw `R*` registers.
## Issue Context
Structured register allocation reduces overlap/corruption bugs and improves maintainability.
## Fix Focus Areas
- academy/spinlock/spinlock_example.asm[37-40]

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


10. XIN/XOUT spinlock example missing 📎 Requirement gap ≡ Correctness
Description
The new spinlock lab demonstrates memory-mapped polling (LBBO/SBCO and volatile pointer loops)
rather than the required local spinlock accelerator flow using XIN polling for acquisition status
and XOUT for release. This fails the requirement to provide documented, working PRU Assembly and C
examples specifically showing the __xin/__xout-based mechanism and register-byte ID/status
usage.
Code

academy/spinlock/readme.md[R51-65]

+### Assembly
+
+The spinlock is accessed by reading and writing to the memory-mapped address. The following assembly snippet shows acquiring lock number 0:
+
+```
+        LBBO &r0, r0, CSL_SPINLOCK0_BASE, 4   ; Read the spinlock register (lock 0)
+        QBEQ acquire, r0, 0                   ; If lock is free (0), we got it
+        SPINLOOP:                             ; Otherwise, spin
+        LBBO &r0, r0, CSL_SPINLOCK0_BASE, 4
+        QBNE SPINLOOP, r0, 0
+        acquire:
+        ; ... critical section ...
+        ; Release the lock by writing 0
+        SBCO &r0, r0, CSL_SPINLOCK0_BASE, 4, 1
+```
Evidence
PR Compliance ID 1 requires acquire via XIN polling against the local spinlock XID and release via
XOUT (or equivalent), with documented ID/status handling. The new lab instead documents and
implements memory-mapped polling loops and writes, and does not show XIN/XOUT or __xin/__xout
usage.

Add HW spinlock usage examples to open-pru/academy in both Assembly and C
academy/spinlock/readme.md[51-65]
academy/spinlock/readme.md[67-85]
academy/spinlock/spinlock_example.p[15-34]
academy/spinlock/spinlock_example.c[36-76]

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 spinlock examples use memory-mapped loads/stores and do not demonstrate the required local spinlock accelerator access using XIN/XOUT (and `__xin`/`__xout`). The C example also relies on `SPINLOCK0_BASE` but does not provide a working, self-contained definition/selection, so it is not clearly buildable as-is.
## Issue Context
Compliance requires two working examples (PRU Assembly and C) demonstrating HW spinlock acquire (XIN-based polling on local spinlock XID) and release (XOUT), including documentation/comments mapping: local spinlock XID, spinlock ID byte location, valid ID range, and how the acquisition status is checked.
## Fix Focus Areas
- academy/spinlock/readme.md[51-87]
- academy/spinlock/spinlock_example.p[1-35]
- academy/spinlock/spinlock_example.c[15-76]

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


11. clpru command uses .p ✓ Resolved 📘 Rule violation ≡ Correctness
Description
The documentation instructs building spinlock_example.p with clpru, but .p is a pasm assembly
extension, not a clpru .asm source. This can mislead users and break builds depending on the
selected toolchain.
Code

academy/spinlock/readme.md[R20-24]

+To build the assembly example, you can use the `clpru` assembler:
+
+```bash
+clpru --silicon_version=3 spinlock_example.p
+```
Evidence
PR Compliance ID 17 requires clpru assembly sources to use .asm and pasm sources to use .p. The
README explicitly instructs using clpru on a .p file, conflicting with this requirement.

academy/spinlock/readme.md[20-24]
academy/spinlock/readme.md[11-12]
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 README shows `clpru` assembling `spinlock_example.p`, but `.p` is intended for pasm, while clpru assembly sources should use `.asm`.
## Issue Context
This repo enforces PRU toolchain/extension conventions to prevent build and user confusion.
## Fix Focus Areas
- academy/spinlock/readme.md[18-26]
- academy/spinlock/spinlock_example.p[1-35]

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


12. Missing TI file headers ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new spinlock_example.c and spinlock_example.p sources do not include the required header
fields including the file name and a Texas Instruments copyright statement. This reduces
traceability and violates the repository’s required legal/header convention.
Code

academy/spinlock/spinlock_example.c[R1-10]

+/*
+ * Spinlock Example - C Version
+ *
+ * This example shows how to use the hardware spinlock module for mutual
+ * exclusion between PRU cores or between PRU and host.
+ *
+ * Note: This example uses direct register access. If your device provides
+ * a Chip Support Layer (CSL) with spinlock APIs, you may prefer to use
+ * those instead for better portability.
+ */
Evidence
PR Compliance ID 15 requires all source files to include a header denoting the file name and a TI
copyright statement. The added C header contains only a descriptive comment without the required
legal text and does not state the file name; the assembly file has no header block at all.

academy/spinlock/spinlock_example.c[1-10]
academy/spinlock/spinlock_example.p[1-14]
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
New source files are missing required headers: explicit file name and Texas Instruments copyright statement.
## Issue Context
Repository compliance requires standardized headers for traceability and legal compliance.
## Fix Focus Areas
- academy/spinlock/spinlock_example.c[1-14]
- academy/spinlock/spinlock_example.p[1-14]

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


13. Spinlock acquire never claims ✓ Resolved 🐞 Bug ≡ Correctness
Description
acquire_spinlock_lock0() returns after observing the register value without performing any
operation that actually claims the lock, so it does not provide mutual exclusion and can race (or
hang) depending on the HW spinlock semantics. This makes the example incorrect for its stated
purpose (protecting a critical section).
Code

academy/spinlock/spinlock_example.c[R40-49]

+static void acquire_spinlock_lock0(volatile uint32_t *spinlock_base)
+{
+    volatile uint32_t *lock0_reg = spinlock_base; /* lock 0 at offset 0 */
+
+    /* Spin until lock is free (value == 0) */
+    while (*lock0_reg != 0) {
+        /* Optionally add a small delay or yield here */
+    }
+    /* Lock acquired */
+}
Evidence
The acquire helper only spins on reading the register and then returns; there is no atomic
claim/test-and-set behavior demonstrated (no write/claim step and no success-value check).

academy/spinlock/spinlock_example.c[40-49]

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

## Issue description
`academy/spinlock/spinlock_example.c` has a spinlock “acquire” helper that only polls a value and then returns; it never performs a lock-claiming operation. This defeats mutual exclusion and can lead to multiple agents entering the critical section or incorrect spinning behavior.
### Issue Context
This file is presented as a hardware spinlock usage example; the acquire function must demonstrate an atomic claim pattern consistent with the device’s spinlock semantics.
### Fix Focus Areas
- academy/spinlock/spinlock_example.c[40-49]
### What to change
- Replace the polling loop with the correct hardware acquire sequence (e.g., the documented “read-to-acquire” loop where a successful acquisition is detected and only then the function returns), and update the comments accordingly.
- Keep `release_spinlock_lock0()` consistent with the acquire semantics (often writing 0 releases, but verify against the intended mechanism for this lab).

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



Remediation recommended

14. Spinlock lab not indexed 🐞 Bug ⚙ Maintainability ⭐ New
Description
The new academy/spinlock lab is not listed in academy/readme.md and is not included in
academy/makefile’s SUBDIRS (which drives the recursive academy build), so it won’t be discoverable
via the academy index and won’t be built by make -C academy. This increases the risk the example
drifts/breaks without being noticed.
Code

academy/spinlock/readme.md[R1-5]

+# Spinlock Usage Examples
+
+This lab shows how a PRU core uses the **PRU_ICSSG local hardware spinlock** to get
+mutual exclusion over a resource shared between PRU0, PRU1 and/or the host CPU.
+
Evidence
The PR introduces a new lab under academy/spinlock, but the academy’s top-level README does not
list it among training labs, and the academy top-level makefile only recurses into the directories
listed in SUBDIRS, which does not include spinlock. Therefore, running the standard academy
build won’t build this new lab, and users browsing the academy README won’t discover it.

academy/spinlock/readme.md[1-5]
academy/readme.md[34-51]
academy/readme.md[78-85]
academy/makefile[3-9]
academy/makefile[24-26]

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

## Issue description
A new lab directory (`academy/spinlock`) was added, but it is not referenced from the academy project index (`academy/readme.md`) and it is not part of the academy recursive build (`academy/makefile` SUBDIRS). As a result, users following the academy entry points won’t find it, and it won’t get built when running the standard academy build.

## Issue Context
`academy/makefile`’s `SUBDIRS` list is used as the set of directories to recurse into for `all/pru/host/clean` targets.

## Fix Focus Areas
- academy/readme.md[34-51]
- academy/readme.md[78-85]
- academy/makefile[3-9]
- academy/makefile[24-26]

## Suggested fix
1. Add `spinlock` to the “Training labs” list in `academy/readme.md`.
2. Add a row for `spinlock` in the supported processors table (or explicitly document it as a source-only lab if you don’t want it in the matrix).
3. If you intend it to be built by `make -C academy`, add `spinlock` to `academy/makefile`’s `SUBDIRS` **and** provide whatever minimal build infrastructure that target expects (so the recursive `$(MAKE) -C spinlock ...` invocation works). If it’s intentionally not built, document that explicitly in the academy index so the omission is not accidental.

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


15. R1 clobbered in helpers 🐞 Bug ☼ Reliability
Description
spinlock_acquire()/spinlock_release() modify R1 without saving/restoring it, which is
discouraged for function-style assembly helpers and can lead to caller register corruption if R1
holds live state across the call. This increases the risk that the example behaves differently
depending on compiler allocation and call context.
Code

academy/spinlock/spinlock.asm[R49-51]

+    MOV         R1.b0, R14.b0           ; lock id -> R1.b0 (presented to accel)
+    XIN         INT_SPIN_XID, &R1.b3, 1 ; status returned in R1.b3 (bit0=acquired)
+    MOV         R14.b0, R1.b3           ; return status
Evidence
The helpers visibly write to R1, and the repo’s best_practices.md documents this exact pattern
(using r1 without saving in a function) as a register corruption pitfall with an explicit
save/restore example.

academy/spinlock/spinlock.asm[48-66]
best_practices.md[859-875]

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 C-callable assembly helpers use `R1.b0`/`R1.b3` as scratch registers but do not preserve `R1`. The repo’s best-practices documentation explicitly calls out “using r1 without saving” as a common register-corruption pitfall for function-like assembly.
## Issue Context
Even if current builds happen to work, this is brittle: changes in compiler register allocation or additional code around the calls can expose the corruption.
## Fix Focus Areas
- academy/spinlock/spinlock.asm[48-66]
## Suggested fix options
- Preferred: save/restore `R1` within each helper (e.g., spill to a known scratch register that is safe in your calling convention, then restore before `JMP r3.w2`).
- Alternative: use only registers that are explicitly caller-saved for PRU C-callable assembly (and document which registers are clobbered).
- Also update the function comment block to list clobbered registers explicitly.

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


16. No lock-id range checks ✓ Resolved 📘 Rule violation ⛨ Security
Description
The C helpers accept an arbitrary lock index and directly index into the spinlock register array
without any validation. An out-of-range lock value can access unintended addresses and corrupt
state.
Code

academy/spinlock/spinlock_example.c[R38-50]

+/* lock N lives at base + N*4; as a uint32_t* index that is simply [N]. */
+#define SPINLOCK_REG(base, n)   (((volatile uint32_t *)(base))[(n)])
+
+/*
+ * Blocking acquire: spin until our read returns 0 (which means we just took it).
+ */
+static void spinlock_acquire(uintptr_t base, uint32_t lock)
+{
+    /* Each read is an atomic test-and-set; a non-zero result means "still held". */
+    while (SPINLOCK_REG(base, lock) != 0U) {
+        /* busy-wait; optionally insert a short delay / NOP here */
+    }
+    /* read returned 0 -> we now own the lock */
Evidence
PR Compliance ID 6 requires sanity checks when performance allows. The macro SPINLOCK_REG(base, n)
directly indexes [(n)] and spinlock_acquire() loops on that value with no validation that lock
is within the device's supported lock range (commonly 0–63).

academy/spinlock/spinlock_example.c[38-50]
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 spinlock helper APIs do not validate the `lock` index (and therefore may read/write outside the valid lock register range).
## Issue Context
Defensive checks are required when feasible to prevent uncontrolled behavior from invalid inputs.
## Fix Focus Areas
- academy/spinlock/spinlock_example.c[38-50]

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


View more (4)
17. e.g. used in docs ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new README uses the disallowed abbreviation e.g. instead of the required phrasing. This
violates the documentation wording standard intended to keep docs consistent and unambiguous.
Code

academy/spinlock/readme.md[R45-47]

+Not every device in the AM2x family exposes the same Spinlock IP to the PRU. If your
+target has no Spinlock module, use an alternative (e.g. a guarded flag in ICSS shared RAM
+with a consistent owner protocol, or a hardware mailbox). Check your TRM first.
Evidence
PR Compliance ID 14 forbids e.g. and requires "for example" instead. The README line includes
(e.g. a guarded flag in ICSS shared RAM ...), which violates the wording standard.

academy/spinlock/readme.md[45-47]
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
Docs must not use `e.g.`/`i.e.`/`etc,` per documentation standards.
## Issue Context
Replace `e.g.` with "for example".
## Fix Focus Areas
- academy/spinlock/readme.md[45-47]

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


18. Misleading build steps ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
academy/spinlock/spinlock_example.asm claims it can be built via gmake -all / CCS PRU project
import, but the repo’s academy aggregate makefile does not include the new spinlock lab directory,
so those instructions are not valid for the standard academy build flow.
Code

academy/spinlock/spinlock_example.asm[R25-27]

+;   Steps to build :
+;   - Using makefile: gmake -all
+;   - Using CCS: import the PRU project, build to get .out and firmware .h
Evidence
The assembly file explicitly instructs gmake -all / CCS project import, but the repository’s
academy aggregate makefile does not list academy/spinlock in its SUBDIRS, while the lab README
documents standalone clpru compilation instead.

academy/spinlock/spinlock_example.asm[25-27]
academy/makefile[3-9]
academy/spinlock/readme.md[49-63]

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

## Issue description
`academy/spinlock/spinlock_example.asm` contains boilerplate build steps (`gmake -all` / CCS project import) that do not apply to this new spinlock lab as currently added, and conflict with the README’s standalone `clpru` commands.
## Issue Context
In this repo, `academy/makefile` controls which labs are built by the aggregate academy build. The new `academy/spinlock` directory is not listed there, so running the academy build will not build this example.
## Fix Focus Areas
- academy/spinlock/spinlock_example.asm[25-27]
- academy/spinlock/readme.md[49-63]
- academy/makefile[3-9]
## Suggested fix
Choose one:
1) **Documentation-only**: Replace the asm file header’s “Using makefile/CCS” text with the same standalone `clpru --silicon_version=... spinlock_example.asm` guidance (or point directly to `academy/spinlock/readme.md`).
2) **Build integration**: Add a proper buildable lab/project structure for `academy/spinlock` and include it in `academy/makefile` `SUBDIRS`, so `make/gmake` builds it as claimed.

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


19. Undefined SPINLOCK0_BASE ✓ Resolved 🐞 Bug ≡ Correctness
Description
spinlock_example.c uses SPINLOCK0_BASE unconditionally, but the file only contains commented-out
candidate #defines, so compiling the example as instructed fails with an undefined macro. This
prevents the example from building out-of-the-box.
Code

academy/spinlock/spinlock_example.c[R63-64]

+    volatile uint32_t *spinlock_base = (volatile uint32_t *)SPINLOCK0_BASE;
+
Evidence
The file uses SPINLOCK0_BASE in main() while both potential #define lines are commented out.
The repo also defines CSL_SPINLOCK0_BASE in its SoC base-address headers, which the example could
reuse instead of an undefined macro.

academy/spinlock/spinlock_example.c[23-25]
academy/spinlock/spinlock_example.c[63-64]
source/include/am243x/cslr_soc_baseaddress.h[412-413]

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 example references `SPINLOCK0_BASE` but does not define it (the only definitions are commented out). This breaks compilation unless the user manually edits the file.
### Issue Context
The repo already carries device base-address headers that define `CSL_SPINLOCK0_BASE`.
### Fix Focus Areas
- academy/spinlock/spinlock_example.c[15-25]
- academy/spinlock/spinlock_example.c[63-64]
### What to change
- Add a compile-time guard:
- If you want users to pass the define via build flags: `#ifndef SPINLOCK0_BASE #error "Define SPINLOCK0_BASE (e.g. CSL_SPINLOCK0_BASE)" #endif`
- Or include the appropriate SoC base-address header and alias: `#define SPINLOCK0_BASE CSL_SPINLOCK0_BASE`.
- Ensure the README build instructions match the chosen approach.

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


20. Misleading PRU assembly snippet ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The README’s PRU assembly snippet uses r0 as the base register without showing it being
initialized to the spinlock base address, and it passes CSL_SPINLOCK0_BASE as an offset into
LBBO/SBCO, which does not match the addressing pattern used in existing PRU assembly code here.
As written, the snippet is incomplete/incorrect and will likely access the wrong address when
followed.
Code

academy/spinlock/readme.md[R56-65]

+        LBBO &r0, r0, CSL_SPINLOCK0_BASE, 4   ; Read the spinlock register (lock 0)
+        QBEQ acquire, r0, 0                   ; If lock is free (0), we got it
+        SPINLOOP:                             ; Otherwise, spin
+        LBBO &r0, r0, CSL_SPINLOCK0_BASE, 4
+        QBNE SPINLOOP, r0, 0
+        acquire:
+        ; ... critical section ...
+        ; Release the lock by writing 0
+        SBCO &r0, r0, CSL_SPINLOCK0_BASE, 4, 1
+```
Evidence
The README snippet dereferences CSL_SPINLOCK0_BASE using r0 as a base register but never shows
r0 being set to that address, and uses the absolute base as an offset. Other PRU assembly in the
repo demonstrates the standard pattern: load the full address into a register and use lbbo/sbbo
with offset 0 for the actual access.

academy/spinlock/readme.md[56-65]
examples/fft/split_radix_fft_4k_single_core/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/lut_load.asm[64-67]
examples/fft/split_radix_fft_4k_single_core/firmware/am243x-lp/icss_g0_pru0_fw/ti-pru-cgt/main.asm[53-56]

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 README’s assembly example does not show loading the spinlock base address into a register before using `LBBO`, and it uses an absolute base address as an immediate offset. This is misleading for readers and inconsistent with how PRU assembly in this repo performs memory-mapped accesses.
### Issue Context
Existing PRU assembly uses `ldi32` to load full addresses into a register, and then uses `lbbo`/`sbbo` with small offsets (often 0) for the access.
### Fix Focus Areas
- academy/spinlock/readme.md[53-65]
### What to change
- Update the snippet to:
- Load the base address into a register (e.g., `ldi32 rX, CSL_SPINLOCK0_BASE`).
- Use `lbbo`/`sbbo` with offset `0` (or `lock_id * 4`) and the correct byte count.
- Show the correct loop structure for acquisition and a correct release store.
- Keep the snippet minimal but complete (initialize base register, show correct access form).

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


Grey Divider

Previous review results

Review updated until commit f22b51c

Results up to commit 3164531


🐞 Bugs (2) 📘 Rule violations (6) 📎 Requirement gaps (1)

Context used

Action required
1. main.c header missing filename 📘 Rule violation ⚙ Maintainability ⭐ New
Description
academy/spinlock/main.c has a copyright/SPDX header but does not denote the file name in the
header section. This violates the required source file header convention and reduces traceability.
Code

academy/spinlock/main.c[R1-16]

+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C) 2024-2026 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Spinlock Example - C Version (PRU_ICSSG local hardware spinlock)
+ *
+ * Demonstrates mutual exclusion between PRU0 and PRU1 using the PRU-local
+ * hardware spinlock, accessed over the broadside interface (XIN/XOUT). The
+ * low-level acquire/release are implemented in spinlock.asm; this file shows
+ * the usage pattern.
+ *
+ * Each PRU repeatedly takes the same spinlock, drives a debug GPO high while it
+ * holds the lock, waits a (per-core different) number of cycles, then releases
+ * and drives the debug GPO low. Probing the two debug pins shows that the two
+ * cores never hold the lock at the same time.
+ */
Evidence
PR Compliance ID 18 requires a file header that includes the file name and TI copyright statement.
The added header in main.c includes SPDX and copyright but does not include a File:/file-name
line identifying main.c.

academy/spinlock/main.c[1-16]
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
`academy/spinlock/main.c` is missing the required header field that denotes the file name.

## Issue Context
The compliance checklist requires each source file header to include both the file name and the TI copyright statement.

## Fix Focus Areas
- academy/spinlock/main.c[1-16]

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


2. TODO marker in main.c 📘 Rule violation ⚙ Maintainability ⭐ New
Description
academy/spinlock/main.c introduces a TODO marker, which is disallowed by the repository marker
standard. This makes tracking inconsistent and violates the required pending-work marker convention.
Code

academy/spinlock/main.c[R35-36]

+/* TODO: pinmux the debug signals (see the GPIO lab) and update the shift to */
+/*       match your board. PRU0 is selected via -DPRU0 in the makefile.      */
Evidence
PR Compliance ID 23 forbids introducing TODO markers. The comment in main.c contains TODO:
indicating pending work.

academy/spinlock/main.c[35-36]
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 code introduces a `TODO` marker; the repository standard requires `FIXME` instead.

## Issue Context
Use `FIXME` for pending work markers to keep consistency across the codebase.

## Fix Focus Areas
- academy/spinlock/main.c[35-36]

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

@qodo-code-review

qodo-code-review Bot commented Jun 13, 2026

Copy link
Copy Markdown

PR Summary by Qodo

Add PRU HW spinlock lab examples (C + assembly) under academy/spinlock
✨ Enhancement 📝 Documentation 🕐 20-40 Minutes

Grey Divider

Walkthroughs

Description
• Add PRU-local HW spinlock usage examples in both C and pure assembly.
• Provide C-callable acquire/release helpers built on XIN/XOUT broadside access.
• Document spinlock semantics, build steps, and correct acquire/release patterns.
Diagram
graph TD
  A["PRU0 firmware"] --> B["main.c"] --> C["spinlock.asm"] --> D{{"HW spinlock (XID 0x90)"}}
  E["PRU1 firmware"] --> B --> F{{"Debug GPO pins"}}
  G["spinlock_example.asm"] --> D
  H["readme.md"] --> B
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Implement acquire/release directly in C via __xin/__xout intrinsics
  • ➕ Removes cross-language linking friction (single-language example)
  • ➕ Easier for C-focused readers to modify/extend
  • ➕ Potentially clearer mapping from source to generated code in one place
  • ➖ Intrinsics usage may be less portable across toolchain versions/configs
  • ➖ Harder to guarantee minimal instruction sequence vs a tiny hand-written asm helper
  • ➖ Still requires explaining register/byte semantics; not necessarily simpler
2. Provide a small reusable spinlock library API (local + remote subsystem variants)
  • ➕ More directly reusable in other labs/projects
  • ➕ Makes the 'local XID 0x90 vs other subsystem XID' distinction explicit in API
  • ➕ Can standardize backoff/yield behavior across examples
  • ➖ More surface area than needed for a focused lab
  • ➖ Requires additional build integration and documentation beyond the issue scope

Recommendation: Keep the current approach: a minimal asm helper for C plus a pure-asm macro example is the best teaching fit and matches the TRM semantics (R1.b0 id, status in R1.b3 bit0, XID 0x90). If this pattern spreads to more labs, consider adding an optional reusable library wrapper later.

Grey Divider

File Changes

Enhancement (3)
main.c Add C contention demo using PRU-local HW spinlock and debug GPO +77/-0

Add C contention demo using PRU-local HW spinlock and debug GPO

• Introduces a PRU C example where PRU0 and PRU1 spin on the same lock ID until acquired, then toggle a debug output while holding the lock. Uses different hold times per core to make contention visible and calls assembly-provided acquire/release helpers.

academy/spinlock/main.c


spinlock.asm Add C-callable spinlock_acquire/spinlock_release using XIN/XOUT +66/-0

Add C-callable spinlock_acquire/spinlock_release using XIN/XOUT

• Implements two small assembly routines that perform a single non-blocking acquire attempt (returning status) and release for a given lock ID. Encodes the PRU C calling convention (argument/return in R14.b0) and uses broadside XID 0x90 for the local spinlock accelerator.

academy/spinlock/spinlock.asm


spinlock_example.asm Add pure-assembly spinlock macros and minimal example +70/-0

Add pure-assembly spinlock macros and minimal example

• Provides M_SPINLOCK_ACQUIRE/M_SPINLOCK_RELEASE macros that busy-wait on the returned status bit (R1.b3 bit0) and release via XOUT. Includes a minimal main that clears registers, acquires, executes a placeholder critical section, releases, and halts.

academy/spinlock/spinlock_example.asm


Documentation (1)
readme.md Document PRU_ICSSG local spinlock semantics, patterns, and build steps +114/-0

Document PRU_ICSSG local spinlock semantics, patterns, and build steps

• Adds lab documentation describing broadside XIN/XOUT access (XID 0x90), register/bit semantics for acquire and release, and correct usage patterns. Includes build commands for assembly-only and mixed C+assembly flows plus guidance on what spinlocks are not meant to do.

academy/spinlock/readme.md


Grey Divider

Qodo Logo

Comment thread academy/spinlock/readme.md Outdated
Comment on lines +51 to +65
### Assembly

The spinlock is accessed by reading and writing to the memory-mapped address. The following assembly snippet shows acquiring lock number 0:

```
LBBO &r0, r0, CSL_SPINLOCK0_BASE, 4 ; Read the spinlock register (lock 0)
QBEQ acquire, r0, 0 ; If lock is free (0), we got it
SPINLOOP: ; Otherwise, spin
LBBO &r0, r0, CSL_SPINLOCK0_BASE, 4
QBNE SPINLOOP, r0, 0
acquire:
; ... critical section ...
; Release the lock by writing 0
SBCO &r0, r0, CSL_SPINLOCK0_BASE, 4, 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

1. xin/xout spinlock example missing 📎 Requirement gap ≡ Correctness

The new spinlock lab demonstrates memory-mapped polling (LBBO/SBCO and volatile pointer loops)
rather than the required local spinlock accelerator flow using XIN polling for acquisition status
and XOUT for release. This fails the requirement to provide documented, working PRU Assembly and C
examples specifically showing the __xin/__xout-based mechanism and register-byte ID/status
usage.
Agent Prompt
## Issue description
The spinlock examples use memory-mapped loads/stores and do not demonstrate the required local spinlock accelerator access using XIN/XOUT (and `__xin`/`__xout`). The C example also relies on `SPINLOCK0_BASE` but does not provide a working, self-contained definition/selection, so it is not clearly buildable as-is.

## Issue Context
Compliance requires two working examples (PRU Assembly and C) demonstrating HW spinlock acquire (XIN-based polling on local spinlock XID) and release (XOUT), including documentation/comments mapping: local spinlock XID, spinlock ID byte location, valid ID range, and how the acquisition status is checked.

## Fix Focus Areas
- academy/spinlock/readme.md[51-87]
- academy/spinlock/spinlock_example.p[1-35]
- academy/spinlock/spinlock_example.c[15-76]

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

Comment thread academy/spinlock/readme.md Outdated
Comment thread academy/spinlock/spinlock_example.c Outdated
Comment on lines +1 to +10
/*
* Spinlock Example - C Version
*
* This example shows how to use the hardware spinlock module for mutual
* exclusion between PRU cores or between PRU and host.
*
* Note: This example uses direct register access. If your device provides
* a Chip Support Layer (CSL) with spinlock APIs, you may prefer to use
* those instead for better portability.
*/

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

3. Missing ti file headers 📘 Rule violation ⚙ Maintainability

The new spinlock_example.c and spinlock_example.p sources do not include the required header
fields including the file name and a Texas Instruments copyright statement. This reduces
traceability and violates the repository’s required legal/header convention.
Agent Prompt
## Issue description
New source files are missing required headers: explicit file name and Texas Instruments copyright statement.

## Issue Context
Repository compliance requires standardized headers for traceability and legal compliance.

## Fix Focus Areas
- academy/spinlock/spinlock_example.c[1-14]
- academy/spinlock/spinlock_example.p[1-14]

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

Comment thread academy/spinlock/spinlock_example.c Outdated
@qodo-code-review

qodo-code-review Bot commented Jun 13, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 035d859

Comment thread academy/spinlock/spinlock_example.asm Outdated
Comment on lines +59 to +65
acquire_lock0:
; Atomic test-and-set: a read of the lock register attempts to take it.
lbbo &LOCK_VAL, SPINLOCK_BASE_REG, LOCK0_OFFSET, 4
; read != 0 -> lock was already held, keep spinning.
; read == 0 -> lock is now OWNED by this PRU, fall through.
qbne acquire_lock0, LOCK_VAL, 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. Busy-wait loop lacks bound 📘 Rule violation ☼ Reliability

The assembly acquire loop can spin forever because it has no timeout or max-iteration termination
condition. This can cause non-deterministic firmware behavior and a hard hang if the lock is never
released.
Agent Prompt
## Issue description
The PRU assembly busy-wait acquire loop has no explicit termination bound, so it can run indefinitely.

## Issue Context
Compliance requires deterministic loops with termination conditions/bounds checking in PRU assembly.

## Fix Focus Areas
- academy/spinlock/spinlock_example.asm[59-65]

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

Comment thread academy/spinlock/spinlock_example.asm Outdated
Comment on lines +37 to +40
; definitions
.asg R2, SPINLOCK_BASE_REG ; holds the Spinlock module base address
.asg R3, LOCK_VAL ; scratch for the value read from / written to the lock

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. Uses .asg register aliases 📘 Rule violation ⚙ Maintainability

The assembly example allocates registers via .asg aliases instead of using .struct/.sassign.
This increases the risk of register overlap/corruption and violates the required structured register
mapping convention.
Agent Prompt
## Issue description
PRU assembly code must map registers using `.struct` and `.sassign`, rather than `.asg` aliases to raw `R*` registers.

## Issue Context
Structured register allocation reduces overlap/corruption bugs and improves maintainability.

## Fix Focus Areas
- academy/spinlock/spinlock_example.asm[37-40]

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

@qodo-code-review

qodo-code-review Bot commented Jun 14, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 3164531

Comment thread academy/spinlock/main.c
Comment on lines +1 to +16
/*
* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2024-2026 Texas Instruments Incorporated - http://www.ti.com/
*
* Spinlock Example - C Version (PRU_ICSSG local hardware spinlock)
*
* Demonstrates mutual exclusion between PRU0 and PRU1 using the PRU-local
* hardware spinlock, accessed over the broadside interface (XIN/XOUT). The
* low-level acquire/release are implemented in spinlock.asm; this file shows
* the usage pattern.
*
* Each PRU repeatedly takes the same spinlock, drives a debug GPO high while it
* holds the lock, waits a (per-core different) number of cycles, then releases
* and drives the debug GPO low. Probing the two debug pins shows that the two
* cores never hold the lock at the same time.
*/

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. Main.c header missing filename 📘 Rule violation ⚙ Maintainability

academy/spinlock/main.c has a copyright/SPDX header but does not denote the file name in the
header section. This violates the required source file header convention and reduces traceability.
Agent Prompt
## Issue description
`academy/spinlock/main.c` is missing the required header field that denotes the file name.

## Issue Context
The compliance checklist requires each source file header to include both the file name and the TI copyright statement.

## Fix Focus Areas
- academy/spinlock/main.c[1-16]

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

Comment thread academy/spinlock/main.c
Comment on lines +35 to +36
/* TODO: pinmux the debug signals (see the GPIO lab) and update the shift to */
/* match your board. PRU0 is selected via -DPRU0 in the makefile. */

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. todo marker in main.c 📘 Rule violation ⚙ Maintainability

academy/spinlock/main.c introduces a TODO marker, which is disallowed by the repository marker
standard. This makes tracking inconsistent and violates the required pending-work marker convention.
Agent Prompt
## Issue description
The code introduces a `TODO` marker; the repository standard requires `FIXME` instead.

## Issue Context
Use `FIXME` for pending work markers to keep consistency across the codebase.

## Fix Focus Areas
- academy/spinlock/main.c[35-36]

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

Comment thread academy/spinlock/main.c
Comment on lines +28 to +29
uint8_t spinlock_acquire(uint8_t flag_id);
void spinlock_release(uint8_t flag_id);

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

3. Multiple spaces in main.c 📘 Rule violation ⚙ Maintainability

academy/spinlock/main.c contains multiple consecutive spaces within declarations for alignment
(for example between void and spinlock_release). This violates the whitespace policy that
restricts whitespace to single spaces and newlines.
Agent Prompt
## Issue description
The file uses multiple consecutive spaces for alignment, but the whitespace policy requires single spaces only.

## Issue Context
This applies to changed content in source files; alignment should be done without introducing multiple spaces (use one space and rely on formatting tools/settings if needed).

## Fix Focus Areas
- academy/spinlock/main.c[28-29]

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

Comment on lines +29 to +51
;******************************************************************************
; Macro: M_SPINLOCK_ACQUIRE
; Acquire the spinlock and busy-wait until successful.
; R1.b0 - Input : spinlock id (set to SPINLOCK_ID)
; R1.b3 - Output: acquisition status (bit 0: 0=failed, 1=acquired)
; Best case 3 cycles; worst case unbounded (busy-wait).
;******************************************************************************
M_SPINLOCK_ACQUIRE .macro
.newblock
LDI R1.b0, SPINLOCK_ID ; lock id (0-63)
$1:
XIN INT_SPIN_XID, &R1.b3, 1 ; attempt acquire; status in R1.b3
QBBC $1, R1.b3, 0 ; bit 0 clear -> not acquired, retry
.endm

;******************************************************************************
; Macro: M_SPINLOCK_RELEASE
; Release the spinlock. Must follow M_SPINLOCK_ACQUIRE (lock id is still
; in R1.b0). 2 cycles.
;******************************************************************************
M_SPINLOCK_RELEASE .macro
XOUT INT_SPIN_XID, &R1.b3, 1 ; release lock held in R1.b0
.endm

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. Macros missing required docs 📘 Rule violation ⚙ Maintainability

The new PRU assembly macros M_SPINLOCK_ACQUIRE and M_SPINLOCK_RELEASE are documented but do not
include all mandatory fields (pseudo code and registers modified). This reduces reviewability and
can hide register-clobber and timing assumptions.
Agent Prompt
## Issue description
PRU assembly macro documentation is missing mandatory fields (pseudo code, registers modified).

## Issue Context
The compliance checklist requires PRU macros/functions to document pseudo code, peak/worst-case cycle budget, and registers modified.

## Fix Focus Areas
- academy/spinlock/spinlock_example.asm[29-51]

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

Comment on lines +49 to +52
MOV R1.b0, R14.b0 ; lock id -> R1.b0 (presented to accel)
XIN INT_SPIN_XID, &R1.b3, 1 ; status returned in R1.b3 (bit0=acquired)
MOV R14.b0, R1.b3 ; return status
JMP r3.w2

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. Acquire return not boolean 🐞 Bug ≡ Correctness

spinlock_acquire() claims to return 0/1 but actually returns the raw R1.b3 status byte; main.c
spins on result != 1, so it can keep spinning even when bit0 indicates the lock is acquired (if
any other status bits are set). This makes the example’s acquire loop depend on an exact byte value
instead of the documented “bit0=success” contract.
Agent Prompt
## Issue description
`spinlock_acquire()` is documented as returning `1` when acquired and `0` otherwise, but it currently returns the entire status byte from `R1.b3`. The C example (`main.c`) and README usage pattern compare the return value to `1`, so the loop is only correct if the hardware always returns exactly `0x00` or `0x01`.

## Issue Context
The README and the pure-assembly macro example both treat only **bit 0** as meaningful (they test the bit), not the entire byte.

## Fix Focus Areas
- academy/spinlock/spinlock.asm[48-52]
- academy/spinlock/main.c[58-63]

## Suggested fix
1. Normalize the return value in `spinlock_acquire()` to `0`/`1` by masking `R1.b3` to bit0 before moving into `R14.b0` (e.g., `AND` with `0x01`).
2. (Optional hardening) Update the C loop to test bit0 (`(spinlock_acquire(...) & 1) != 0`) so the usage pattern matches the documented semantics even if the helper is reused elsewhere.

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

Closes TexasInstruments#120.

Demonstrates the PRU_ICSSG local hardware spinlock accessed over the broadside
interface (XIN/XOUT), as recommended in the issue:

  - lock id (0-63) in R1.b0
  - acquire: XIN 0x90, &R1.b3, 1  -> status in R1.b3 bit 0 (1 = acquired)
  - release: XOUT 0x90, &R1.b3, 1 (same id in R1.b0)

Files:
  - spinlock.asm: C-callable spinlock_acquire()/spinlock_release().
  - main.c: C example - PRU0/PRU1 contend for one lock, scope via debug GPO.
  - spinlock_example.asm: pure-asm example with M_SPINLOCK_ACQUIRE/RELEASE macros.
  - readme.md: usage, semantics, and build notes.

All sources assemble/compile with clpru --silicon_version=3 (ti-cgt-pru 2.3.3);
main.c builds for both PRU0 and PRU1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pratheesh pratheesh force-pushed the fix/issue-120-spinlock-examples branch from 3164531 to f22b51c Compare June 14, 2026 00:16
@qodo-code-review

qodo-code-review Bot commented Jun 14, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit f22b51c

Comment on lines +8 to +21
The PRU_ICSSG includes a hardware spinlock accelerator with **64 ownership flags**
(lock id `0`–`63`). A PRU core reaches it over the **broadside interface** using the
`XIN`/`XOUT` instructions — there is no memory-mapped load/store on the critical path,
so acquire/release are only a couple of cycles.

- The broadside device id (XID) **`0x90` (= 144)** selects the spinlock in the
**local** PRU subsystem. A different XID is needed to reach a spinlock in another
PRU subsystem.
- The **lock id** to operate on is placed in **`R1.b0`**.
- **Acquire** (`XIN 0x90, &R1.b3, 1`): the accelerator returns the acquisition status
in **`R1.b3`** — **bit 0 = 1** means the lock is now owned by this core, **bit 0 = 0**
means it is held by someone else, so retry. A single `XIN` is one attempt; busy-wait
until you get a 1.
- **Release** (`XOUT 0x90, &R1.b3, 1`): with the same lock id still in `R1.b0`, frees

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. Busy-wait worst-case undocumented 📎 Requirement gap ☼ Reliability

The README describes spinning until acquisition succeeds but does not explicitly document that the
busy-wait can be unbounded in the worst case, as required. This can mislead users about
timing/determinism implications of the blocking acquire loop.
Agent Prompt
## Issue description
The documentation explains the status bit and that the PRU should busy-wait, but it does not explicitly state the worst-case acquisition time is unbounded.

## Issue Context
PR Compliance requires documenting busy-wait semantics including best/worst-case timing characteristics and how acquisition success is detected.

## Fix Focus Areas
- academy/spinlock/readme.md[8-22]

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

Comment on lines +35 to +66
;******************************************************************************
; uint8_t spinlock_acquire(uint8_t flag_id);
;
; One non-blocking attempt to acquire spinlock 'flag_id'.
; Returns 1 if the lock was acquired, 0 if it is held by someone else.
;
; Calling convention (PRU C/C++ compiler): the uint8_t argument arrives in
; R14.b0 and the uint8_t return value is passed back in R14.b0. The return
; address is in r3.w2.
;******************************************************************************
.sect ".text:spinlock_acquire"
.clink
.global spinlock_acquire
spinlock_acquire:
MOV R1.b0, R14.b0 ; lock id -> R1.b0 (presented to accel)
XIN INT_SPIN_XID, &R1.b3, 1 ; status returned in R1.b3 (bit0=acquired)
MOV R14.b0, R1.b3 ; return status
JMP r3.w2

;******************************************************************************
; void spinlock_release(uint8_t flag_id);
;
; Release spinlock 'flag_id'. Only the owner should release, and it must do
; so promptly.
;******************************************************************************
.sect ".text:spinlock_release"
.clink
.global spinlock_release
spinlock_release:
MOV R1.b0, R14.b0 ; lock id -> R1.b0
XOUT INT_SPIN_XID, &R1.b3, 1 ; release the lock
JMP r3.w2

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. spinlock.asm missing required metadata 📘 Rule violation ⚙ Maintainability

The new assembly functions spinlock_acquire/spinlock_release are added without the mandatory
function metadata (pseudo code, peak/worst-case cycles, and registers modified). This reduces
reviewability and can hide timing/register-clobber assumptions in a low-level synchronization
primitive.
Agent Prompt
## Issue description
The assembly functions added in `spinlock.asm` do not include the required metadata fields: pseudo code, peak/worst-case cycle count, and registers modified.

## Issue Context
The compliance checklist requires standardized documentation for PRU assembly functions/macros to prevent timing and register-corruption bugs.

## Fix Focus Areas
- academy/spinlock/spinlock.asm[35-66]

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

Add the required PRU assembly function metadata (pseudocode, registers
modified, peak cycle count) to spinlock_acquire and spinlock_release,
matching the repo convention (e.g. multicore_scheduler pwm_toggle.asm).
Documents timing and register-clobber assumptions for the low-level
synchronization primitive. Comments only; assembles with clpru 2.3.3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pratheesh

Copy link
Copy Markdown
Contributor Author

Added the required PRU assembly function metadata to spinlock_acquire/spinlock_release in spinlock.asm (b8f1339): pseudocode, registers modified, and peak cycle count, following the repo convention (e.g. multicore_scheduler/.../pwm_toggle.asm). This documents the timing and register-clobber assumptions for the primitive. Comments-only; assembles with clpru 2.3.3 and CI is green.

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.

Create HW spinlock usage examples both assembly/C in open-pru/academy

1 participant