Skip to content

CRITICAL BUG: HDLC Controller KeyValue=0x0000 End Marker Issue

Root Cause Identified in NDBusHDLC.cs

The Critical Bug in ReadNextBuffer() Function

Line 3150-3153:

internal DmaBufferDescription ReadNextBuffer(DmaBufferDescription prev)
{
    return LoadBufferDescription(prev.ListPointer + 4);
}

The Problem: No KeyValue=0x0000 Detection

The ReadNextBuffer() function blindly advances to the next buffer without checking if the current buffer has KeyValue=0x0000 (end marker).

Expected Behavior:

internal DmaBufferDescription ReadNextBuffer(DmaBufferDescription prev)
{
    // Check if current buffer is end marker
    if (prev.KeyValue == 0x0000)
    {
        SetTXDMAFlag(TransmitterStatusBits.ListEnd | TransmitterStatusBits.TransmissionFinished);
        return prev; // Stay at end marker, don't advance
    }

    return LoadBufferDescription(prev.ListPointer + 4);
}

Actual Behavior:

  1. Processes valid buffer with data → ✓ Works correctly
  2. Encounters KeyValue=0x0000 → ❌ IGNORES IT, advances anyway
  3. Reads garbage memory → ❌ Treats as valid buffer
  4. DMA continues with invalid data → ❌ No ListEnd set

Evidence from DMA_SendChar() Function

Lines 2758-2762:

var tmp = ReadNextBuffer(regs.DMA_TX_BufferDescription);
if (tmp.Key != KeyFlags.BlockToBeTransmitted)
{
    SetTXDMAFlag(TransmitterStatusBits.TransmissionFinished| TransmitterStatusBits.ListEnd);
}

The Logic is Backwards: - Checks AFTER reading next buffer instead of checking CURRENT buffer - KeyValue=0x0000 is NOT KeyFlags.BlockToBeTransmitted but gets processed anyway - ListEnd only set when NEXT buffer is invalid instead of when current buffer is end marker

Trace Analysis Correlation

Why Newer Trace Shows "Correct" Behavior:

The newer trace shows KeyValue=0x0000 and ListEnd being set because:

  1. Buffer preparation includes proper end markers
  2. OS writes KeyValue=0x0000 correctly
  3. DMA eventually encounters non-transmittable buffer (by luck/timing)
  4. ListEnd gets set accidentally when invalid buffer found

Why Earlier Trace Shows DMA Stopping:

  1. DMA reads past end marker into garbage memory
  2. Garbage memory interpreted as invalid buffer type
  3. Accidental ListEnd triggers when garbage isn't BlockToBeTransmitted
  4. Appears to work, but only by accident

The Correct Fix

1. Fix ReadNextBuffer() Function:

internal DmaBufferDescription ReadNextBuffer(DmaBufferDescription prev)
{
    // DON'T advance if current buffer is end marker
    if (prev.KeyValue == 0x0000)
    {
        // Signal proper DMA completion
        SetTXDMAFlag(TransmitterStatusBits.ListEnd | TransmitterStatusBits.TransmissionFinished);

        // Return current buffer (don't advance)
        return prev;
    }

    // Normal advancement to next buffer
    return LoadBufferDescription(prev.ListPointer + 4);
}

2. Fix DMA_SendChar() Logic:

// Check CURRENT buffer for end marker BEFORE processing
if (regs.DMA_TX_BufferDescription.KeyValue == 0x0000)
{
    SetTXDMAFlag(TransmitterStatusBits.ListEnd | TransmitterStatusBits.TransmissionFinished);
    dmaSenderState = DMA_SenderState.IDLE;
    return; // Stop processing
}

// Only process if current buffer is valid
if (regs.DMA_TX_BufferDescription.Key == KeyFlags.BlockToBeTransmitted)
{
    // Normal transmission logic...
}

3. Add Buffer Validation in LoadBufferDescription():

internal DmaBufferDescription LoadBufferDescription(uint list_pointer)
{
    // ... existing code ...

    description.KeyValue = (ushort)DMARead(address++);

    // Detect end marker immediately
    if (description.KeyValue == 0x0000)
    {
        Log($"End marker (KeyValue=0x0000) detected at 0x{list_pointer:X6}");
        description.Key = KeyFlags.EndOfList; // Add new enum value
    }

    // ... rest of function ...
}

Impact Analysis

Current Bug Symptoms:

  • Intermittent DMA stopping - depends on memory content past end marker
  • Accidental ListEnd triggers - when garbage memory isn't transmittable
  • Memory corruption risk - reading/writing past valid buffer list
  • Unpredictable behavior - success/failure depends on memory layout

After Fix:

  • Deterministic ListEnd - always triggered by KeyValue=0x0000
  • Proper DMA completion - no reading past valid buffers
  • Reliable interrupts - Level 12 interrupt always fires correctly
  • Predictable OS synchronization - SINTRAN always notified properly

Debugging Evidence

The trace showing "correct" behavior is actually masking the bug: - ListEnd appears to work due to accidental triggering - KeyValue=0x0000 detected but processed incorrectly - DMA stops due to garbage memory, not proper end detection

The real fix requires explicit KeyValue=0x0000 handling before any buffer advancement occurs.

Priority: CRITICAL

This bug explains: - Random DMA stopping behavior - Missing transmission opportunities - OS/DMA synchronization issues - Inconsistent HDLC frame transmission

Fix immediately to ensure reliable HDLC communication.