Add protections to GEX to prevent ADC DMA overrun, shows that the previously tested high speeds were not real
This commit is contained in:
+21
-8
@@ -11,9 +11,6 @@
|
||||
|
||||
#define DMA_POS(priv) ((priv)->dma_buffer_itemcount - (priv)->DMA_CHx->CNDTR)
|
||||
|
||||
volatile bool emergency = false;
|
||||
//#define CRUMB() if(emergency) trap("crumb")
|
||||
|
||||
static void UADC_JobSendBlockChunk(Job *job)
|
||||
{
|
||||
Unit *unit = job->unit;
|
||||
@@ -23,7 +20,8 @@ static void UADC_JobSendBlockChunk(Job *job)
|
||||
|
||||
uint32_t start = job->data1;
|
||||
uint32_t count = job->data2;
|
||||
bool close = (bool) job->data3;
|
||||
bool close = (bool) (job->data3 & 0x80);
|
||||
bool tc = (bool) (job->data3 & 0x01);
|
||||
|
||||
// dbg("Send indices [%d -> %d)", (int)start, (int)(start+count));
|
||||
|
||||
@@ -40,6 +38,9 @@ static void UADC_JobSendBlockChunk(Job *job)
|
||||
TF_Multipart_Payload(comm, (uint8_t *) (priv->dma_buffer + start), count * sizeof(uint16_t));
|
||||
TF_Multipart_Close(comm);
|
||||
|
||||
if (tc) priv->tc_pending = false;
|
||||
else priv->ht_pending = false;
|
||||
|
||||
priv->stream_serial++;
|
||||
}
|
||||
|
||||
@@ -179,27 +180,36 @@ static void handle_httc(Unit *unit, bool tc)
|
||||
|
||||
bool close = !m_stream && priv->trig_stream_remain == 0;
|
||||
|
||||
if ((tc && priv->tc_pending) || (ht && priv->ht_pending)) {
|
||||
dbg("(!) DMA not handled in time, abort capture");
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN);
|
||||
return;
|
||||
}
|
||||
|
||||
Job j = {
|
||||
.unit = unit,
|
||||
.data1 = start,
|
||||
.data2 = sgcount * priv->nb_channels,
|
||||
.data3 = (uint32_t) close,
|
||||
.data3 = (uint32_t) (close*0x80) | (tc*1),
|
||||
.cb = UADC_JobSendBlockChunk
|
||||
};
|
||||
|
||||
if (tc)
|
||||
priv->tc_pending = true;
|
||||
else
|
||||
priv->ht_pending = true;
|
||||
|
||||
if (!scheduleJob(&j)) {
|
||||
// Abort if we can't queue - the stream would tear and we'd hog the system with error messages
|
||||
dbg("(!) Buffers overflow, abort capture");
|
||||
emergency = true;
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (close) {
|
||||
// dbg("End of capture");
|
||||
// If auto-arm enabled, we need to re-arm again.
|
||||
// However, EOS irq is disabled during the capture.
|
||||
// We have to wait for the next EOS interrupt to occur.
|
||||
// TODO verify if keeping the EOS irq enabled during capture has significant performance penalty. If not, we can leave it enabled.
|
||||
UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE);
|
||||
}
|
||||
} else {
|
||||
@@ -499,6 +509,9 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode)
|
||||
// IDLE and ARMED are identical with the exception that the trigger condition is not checked
|
||||
// ARMED can be only entered from IDLE, thus we do the init only here.
|
||||
|
||||
priv->tc_pending = false;
|
||||
priv->ht_pending = false;
|
||||
|
||||
// In IDLE, we don't need the DMA interrupts
|
||||
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
|
||||
@@ -74,7 +74,10 @@ struct priv {
|
||||
bool auto_rearm; //!< Flag that the trigger should be re-armed after the stream finishes
|
||||
uint16_t trig_holdoff; //!< Trigger hold-off time, set when configuring the trigger
|
||||
TF_ID stream_frame_id; //!< Session ID for multi-part stream (response or report)
|
||||
uint8_t stream_serial;
|
||||
uint8_t stream_serial; //!< Serial nr of a stream frame
|
||||
|
||||
bool tc_pending;
|
||||
bool ht_pending;
|
||||
};
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
|
||||
Reference in New Issue
Block a user