TMS320F28374S: ADC SAMPLE

Part Number: TMS320F28374S

In the CCS project for the TMS320F28374S, adding or removing a handle—which theoretically does not affect ADC sampling or the control loop execution, and would not even be invoked at runtime—results in a fixed bias in the actual output current of the charging module. This occurs under the strict condition that only the code has been updated, with absolutely no changes made to the hardware->What could be the reason for this? 

  • Hi Liu,

    The issue is not caused by the handle's logic—it's caused by the compiler and linker reorganizing memory layout when any code is added or removed, which corrupts or shifts critical control-loop variables.

    Mechanism

    On the TMS320F28374S, when you add or remove code (even dead code), the linker reassigns memory addresses for all variables not explicitly pinned to a specific location. This can cause:

    1. Memory allocation conflicts — Variables used in the ADC sampling or current control loop get relocated to addresses where they overlap with or are corrupted by other memory operations (e.g., DMA, CLA, or stack activity) 1.

    2. Execution timing shifts — If the control loop runs from Flash, code reorganization changes alignment and cache/prefetch behavior, altering the ADC-to-PWM latency. In fast current loops, even sub-microsecond timing changes manifest as a consistent output bias 2. Flash sections require 128-bit alignment, and any code size change can shift where critical functions land relative to Flash bank boundaries and wait-state boundaries 3.

    3. Optimization-dependent variable corruption — A directly analogous case on the same device (TMS320F28374S) showed that adding/removing a function caused a counter variable to behave abnormally (range collapsed from 0–0x7F to 0–4), purely due to linker memory reallocation. The issue was sensitive to optimization level and disappeared when variables were explicitly assigned to memory sections 1.

    Solution

    Action Purpose
    Use #pragma DATA_SECTION(variable, "section_name") for all critical control-loop and ADC-related variables Prevents linker from relocating them when code changes
    Assign time-critical control functions to .TI.ramfunc section Eliminates Flash wait-state/alignment timing sensitivity
    Enable the -w linker option and explicitly place all sections in the .cmd file Prevents default allocation algorithm from making unpredictable placements 4
    Compare .map files between the two builds Identifies exactly which variables/functions shifted addresses

    Diagnostic Step

    Generate the linker .map file for both builds (with and without the handle) and diff them. Look for address changes in variables related to your current reference, PI controller state, ADC result buffers, or calibration offsets. The variable whose address shifted into a problematic region is the source of your fixed bias.

    Follow up: Have you compared the linker .map files between the two builds to identify which specific control-loop variables or functions changed memory addresses?


    1. TMS320F28374S: .cmd file configuration and optimization level will affect the value of variable
    2. Fast Current Loop Library Application Note (SPRACB8A)
    3. TMS320F2837xS Technical Reference Manual (SPRUHX5I)
    4. Running an Application from Internal Flash Memory on the TMS320F28xxx (SPRA958L)

    Thanks

    Srikanth

  • I'm having trouble understanding this. After comparing the map files, it seems like the addresses of almost all variables in the code have shifted. How can I specifically identify which variables were relocated into the problematic memory regions?

  • Hi Liu,

    Our memory SME will get back to you on this in 24 hrs.

    Thanks

    Srikanth

  • Hey Liu,

    You're right that when code is added/removed, the linker shifts nearly everything. The key is not to find which variables moved (they all did), but to find which ones moved into a memory region with access conflicts or timing issues. Here's how to narrow it down systematically:

    Step 1: Filter by Function, Not by Address Change

    In your .map file diff, ignore variables unrelated to the control loop. Focus only on these categories:

    Variable Category Examples
    ADC result buffers Raw ADC readings, averaged samples
    Current/voltage references Iref, Vref, setpoints
    PI/PID controller state Integral accumulator, error terms, output
    Calibration offsets ADC offset, gain correction values
    PWM duty cycle variables Duty output, compare values

    Step 2: Check for Memory Region Boundary Crossings

    For each variable above, check whether its new address crossed into a problematic region 1:

    • LSx RAM with MSEL configured for CLA sharing — If a CPU-only variable landed in an LS RAM block where CLA has data access, the CLA may overwrite it 2
    • GSx RAM blocks used by DMA — If a control variable moved into a GS RAM region where DMA is actively transferring ADC results, DMA writes can corrupt it 2
    • Stack overflow proximity — If variables moved closer to the stack boundary, stack growth during ISR nesting could corrupt them

    Step 3: The Definitive Test

    Rather than hunting through hundreds of address changes, pin your critical variables and confirm the fix:

    // Pin ADC and control-loop variables to a dedicated section
    #pragma DATA_SECTION(adcResult, "ctrl_data")
    #pragma DATA_SECTION(piIntegral, "ctrl_data")
    #pragma DATA_SECTION(currentRef, "ctrl_data")
    #pragma DATA_SECTION(dutyOutput, "ctrl_data")
    
    // In your .cmd file, assign this section to a fixed, conflict-free RAM block:
    SECTIONS
    {
        ctrl_data : > RAMGS4, PAGE = 1  // Use a GS RAM block NOT used by DMA
    }

    Then add/remove the handle again. If the bias disappears, you've confirmed the root cause. If it persists, also move your control ISR function to RAM:

    #pragma CODE_SECTION(controlLoopISR, ".TI.ramfunc")

    This eliminates Flash alignment/wait-state timing shifts that cause consistent bias in fast current loops 1.

    Summary

    You don't need to identify the exact variable that moved into a bad region. The practical approach is:

    1. Pin all control-critical variables to an explicit, conflict-free memory section using #pragma DATA_SECTION
    2. Move the control ISR to RAM using .TI.ramfunc
    3. Rebuild with and without the handle — the bias should now be eliminated regardless of code changes

    This was the exact resolution for an analogous TMS320F28374S case where variable behavior changed purely due to linker reallocation 1.


    1. TMS320F28374S: .cmd file configuration and optimization level will affect the value of variable
    2. TMS320F2837xS Technical Reference Manual (SPRUHX5I)

    Best Regards,

    Zackary Fleenor

  • Hi,fleenor:

    The interrupt function is already placed in .TI.ramfunc, but the issue disappeared after we also moved another function called within the interrupt to .TI.ramfunc。

  • Hi Liu,

    Your finding confirms the root cause. Even though your ISR itself was in RAM, the function it called was still in Flash. When the CPU branches from RAM into Flash mid-ISR, it incurs wait-state penalties that introduce a fixed timing skew in your control loop 1.

    Why This Causes a Fixed Bias

    In a fast current loop, the real interrupt latency is the aggregate of: trigger delay + ADC sampling + hardware response + context save + all code execution within the ISR 1. When you add or remove code (even dead code), the linker shifts where that nested function lands in Flash. This changes its alignment relative to 128-bit Flash bank boundaries and prefetch/cache line boundaries 2. The result is a different—but consistent—number of wait-state cycles on every ISR execution, which shifts the ADC-to-PWM update timing by a fixed amount, manifesting as a constant current bias.

    The Fix (What You Already Did)

    Moving the called function to .TI.ramfunc eliminates Flash access entirely from the ISR execution path. RAM has zero wait states, so the timing becomes deterministic regardless of code changes elsewhere in the project 2.

    Best Practice Going Forward

    Ensure the entire call chain within any time-critical ISR resides in RAM—not just the top-level ISR function. Any function called from the ISR (and any function those call) must also be placed in .TI.ramfunc:

    #pragma CODE_SECTION(controlLoopISR, ".TI.ramfunc")
    #pragma CODE_SECTION(nestedFunction, ".TI.ramfunc")
    // Include ALL functions in the ISR call tree

    This makes your control loop immune to linker-induced Flash alignment changes when code is added or removed.


    1. C2000 Real-Time Control Reference Guide (SPRACW5A)
    2. TMS320F2837xS Technical Reference Manual (SPRUHX5I)
    3. TMS320F28374S Upgrade Issue - E2E Forum

    Best Regards,

    Zackary Fleenor