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:¶
- Processes valid buffer with data → ✓ Works correctly
- Encounters KeyValue=0x0000 → ❌ IGNORES IT, advances anyway
- Reads garbage memory → ❌ Treats as valid buffer
- 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:
- Buffer preparation includes proper end markers
- OS writes KeyValue=0x0000 correctly
- DMA eventually encounters non-transmittable buffer (by luck/timing)
- ListEnd gets set accidentally when invalid buffer found
Why Earlier Trace Shows DMA Stopping:¶
- DMA reads past end marker into garbage memory
- Garbage memory interpreted as invalid buffer type
- Accidental ListEnd triggers when garbage isn't
BlockToBeTransmitted - 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.