/* * interrupt.c * * Created on: Aug 29, 2019 * Author: abody */ #include #include #if defined(HAVE_DIAG) #include "diag.h" #endif #include "f4ll_cpp/dmahelper.h" #ifndef DIAG_CRC_CALC_START # define DIAG_CRC_CALC_START() #endif #ifndef DIAG_CRC_CALC_END # define DIAG_CRC_CALC_END() #endif #ifndef DIAG_INTERRUPT_IN # define DIAG_INTERRUPT_IN() #endif #ifndef DIAG_INTERRUPT_OUT # define DIAG_INTERRUPT_OUT() #endif namespace f4ll_cpp { void Crc_StartNextTask(struct crcstatus_t *status); CrcScheduler::CrcScheduler(CRC_TypeDef *crcUnit, DMA_TypeDef *dma, uint32_t stream) : m_dma(dma, stream), m_activeSlot(nullptr), m_firstSlot(nullptr) { m_crcUnit = crcUnit; LL_DMA_EnableIT_TC(dma, stream); LL_DMA_EnableIT_TE(dma, stream); LL_DMA_SetM2MDstAddress(dma, stream, (uint32_t)&crcUnit->DR); } void CrcScheduler::AttachTasks(struct crcslot_t *slot, struct crctask_t *tasks, uint8_t taskCount) { slot->count = taskCount; slot->tasks = tasks; memset(tasks, 0, sizeof(*tasks)*taskCount); uint32_t prim = __get_PRIMASK(); __disable_irq(); slot->next = m_firstSlot; m_firstSlot = slot; __set_PRIMASK(prim); } uint8_t CrcScheduler::GetActiveTask(struct crcslot_t **slot_out) { uint8_t ret; uint32_t prim = __get_PRIMASK(); __disable_irq(); ret = m_activeTask; if(slot_out) *slot_out = (struct crcslot_t *) m_activeSlot; __set_PRIMASK(prim); return ret; } bool CrcScheduler::IsTaskQueued(struct crcslot_t *slot, uint8_t task) { return ((struct crctask_t volatile)slot->tasks[task]).address != NULL; } bool CrcScheduler::IsTaskBusy(struct crcslot_t *slot, uint8_t task) { struct crctask_t volatile *taskPtr = &slot->tasks[task]; return taskPtr->callback != NULL || taskPtr->callbackParam != NULL; } void CrcScheduler::WaitResults(struct crcslot_t *slot, uint8_t task) { while(IsTaskBusy(slot, task)); } uint8_t CrcScheduler::Enqueue(struct crcslot_t *slot, uint8_t task, void *address, uint16_t len, ICrcCallback* callback, void* callbackParam) { uint32_t prim = __get_PRIMASK(); bool need_start; //struct crcstatus_t volatile *st = status; while(IsTaskBusy(slot, task)); __disable_irq(); need_start = (m_activeSlot == nullptr); slot->tasks[task].address = need_start ? NULL : address; slot->tasks[task].wordCount = (len+3)/4; slot->tasks[task].callback = callback; slot->tasks[task].callbackParam = callbackParam; if(need_start) { m_activeSlot = slot; m_activeTask = task; } __set_PRIMASK(prim); if(need_start) { DIAG_CRC_CALC_START(); m_crcUnit->CR = 1; LL_DMA_SetM2MSrcAddress(m_dma.GetDma(), m_dma.GetStream(), (uint32_t)address); LL_DMA_SetDataLength(m_dma.GetDma(), m_dma.GetStream(), (len+3)/4); DIAG_CRC_CALC_START(); LL_DMA_EnableStream(m_dma.GetDma(), m_dma.GetStream()); } return need_start; } uint32_t CrcScheduler::Compute(struct crcslot_t *slot, uint8_t task, void *address, uint16_t len) { uint32_t result; Enqueue(slot, task, address, len, NULL, &result); while((struct crcslot_t volatile *)slot->tasks[task].callbackParam); return result; } // only called from ISR context void CrcScheduler::StartNextTask() { char moreTasks; uint8_t index = 0; do { struct crcslot_t *slot = m_firstSlot; moreTasks = 0; while(slot) { if(index < slot->count) { if(slot->tasks[index].address) { DIAG_CRC_CALC_START(); m_activeSlot = slot; m_activeTask = index; m_crcUnit->CR = 1; LL_DMA_SetM2MSrcAddress(m_dma.GetDma(), m_dma.GetStream(), (uint32_t)slot->tasks[index].address); LL_DMA_SetDataLength(m_dma.GetDma(), m_dma.GetStream(), slot->tasks[index].wordCount); LL_DMA_EnableStream(m_dma.GetDma(), m_dma.GetStream()); slot->tasks[index].address = nullptr; // marking as started return; } if(index + 1 < slot->count) moreTasks = 1; } slot = slot->next; } ++index; } while(moreTasks); m_activeSlot = nullptr; } // !!!PORTABILITY WARNING!!! using registers and bits directly. should be reviewed extremely when porting to a different MCU void CrcScheduler::_HandleDmaIrq() { uint8_t success = 1; DIAG_INTERRUPT_IN(); if((*m_dma.GetIsReg() & m_dma.GetTcMask()) || (*m_dma.GetIsReg() & m_dma.GetTeMask())) { if(*m_dma.GetIsReg() & m_dma.GetTeMask()) success = 0; *m_dma.GetIfcReg() = *m_dma.GetIsReg() & (m_dma.GetTcMask() | m_dma.GetTeMask()); LL_DMA_DisableStream(m_dma.GetDma(), m_dma.GetStream()); if(m_activeSlot) { crctask_t *tsk = &m_activeSlot->tasks[m_activeTask]; if(tsk->callback) tsk->callback->CrcCalculationCompleted(tsk->callbackParam, m_crcUnit->DR, success); else if(tsk->callbackParam) *(uint32_t*)tsk->callbackParam = success ? m_crcUnit->DR : 0xffffffff; tsk->callback = nullptr; tsk->callbackParam = nullptr; // marking as inactive DIAG_CRC_CALC_END(); StartNextTask(); } } DIAG_INTERRUPT_OUT(); } } // f4ll_cpp