|
/**
|
|
* \file tcDspUpp.cpp
|
|
*
|
|
* \brief DSP uPP driver source
|
|
*
|
|
* o 0
|
|
* | / Copyright (c) 2005-2011
|
|
* (CL)---o Critical Link, LLC
|
|
* \
|
|
* O
|
|
*/
|
|
|
|
#include "DspUpp.h"
|
|
#include "memorymap.h"
|
|
#include "core/DspSyscfg.h"
|
|
#include "core/DspLpsc.h"
|
|
|
|
#include <tsk.h>
|
|
#include <assert.h>
|
|
#include <hwi.h>
|
|
#include <c62.h>
|
|
|
|
using namespace MityDSP;
|
|
|
|
SEM_Handle tcDspUpp::mhGetInstSem = SEM_create(1, NULL);
|
|
|
|
tcDspUpp* tcDspUpp::mpDspUpp = NULL;
|
|
|
|
/**
|
|
* Get instance of tcDspUpp.
|
|
*/
|
|
tcDspUpp*
|
|
tcDspUpp::getInstance()
|
|
{
|
|
// Pend on the static mutex so that we do not accidentally
|
|
// init too many objects
|
|
SEM_pend(mhGetInstSem, SYS_FOREVER);
|
|
|
|
// Check if the singleton need initialization
|
|
if (NULL == mpDspUpp)
|
|
{
|
|
mpDspUpp = new tcDspUpp();
|
|
}
|
|
|
|
// Safe to return the mutex now
|
|
SEM_post(mhGetInstSem);
|
|
|
|
// Return pointer to singleton
|
|
return mpDspUpp;
|
|
}
|
|
|
|
/**
|
|
* Intiailize the uPP device.
|
|
*/
|
|
int
|
|
tcDspUpp::initialize(tsDspUppConfig const* apDspUppConfig)
|
|
{
|
|
// ISR attributes
|
|
HWI_Attrs hwi_attrs = {0, 0, (Arg)this};
|
|
|
|
TSK_Attrs tsk_attrs = TSK_ATTRS;
|
|
|
|
tuUppcrReg luUppcrReg = {0};
|
|
tuUpctlReg luUpctlReg = {0};
|
|
tuUpicrReg luUpicrReg = {0};
|
|
tuUpivrReg luUpivrReg = {0};
|
|
tuUptcrReg luUptcrReg = {0};
|
|
tuUpiesReg luUpiesReg = {0};
|
|
|
|
// Pin Mux functions to make sure uPP is enabled properly for Channel A
|
|
tePinFunc laPinFuncCHA[] =
|
|
{
|
|
UPP_CH1_WAIT,
|
|
UPP_CH1_ENABLE,
|
|
UPP_CH1_START,
|
|
UPP_CH1_CLK,
|
|
PINFUNC_LIST_TERMINATE
|
|
};
|
|
|
|
// Pin Mux functions to make sure uPP is enabled properly for Channel B
|
|
tePinFunc laPinFuncCHB[] =
|
|
{
|
|
UPP_CH0_WAIT,
|
|
UPP_CH0_ENABLE,
|
|
UPP_CH0_START,
|
|
UPP_CH0_CLK,
|
|
PINFUNC_LIST_TERMINATE
|
|
};
|
|
|
|
// Pin Mux functions for DATA[15:8]
|
|
tePinFunc laPinFuncData15_8[] =
|
|
{
|
|
UPP_D8,
|
|
UPP_D9,
|
|
UPP_D10,
|
|
UPP_D11,
|
|
UPP_D12,
|
|
UPP_D13,
|
|
UPP_D14,
|
|
UPP_D15,
|
|
PINFUNC_LIST_TERMINATE
|
|
};
|
|
|
|
// Pin Mux functions for DATA[7:0]
|
|
tePinFunc laPinFuncData7_0[] =
|
|
{
|
|
UPP_D0,
|
|
UPP_D1,
|
|
UPP_D2,
|
|
UPP_D3,
|
|
UPP_D4,
|
|
UPP_D5,
|
|
UPP_D6,
|
|
UPP_D7,
|
|
PINFUNC_LIST_TERMINATE
|
|
};
|
|
|
|
// Pin Mux functions for XDATA[15:8]
|
|
tePinFunc laPinFuncXData15_8[] =
|
|
{
|
|
UPP_XD8,
|
|
UPP_XD9,
|
|
UPP_XD10,
|
|
UPP_XD11,
|
|
UPP_XD12,
|
|
UPP_XD13,
|
|
UPP_XD14,
|
|
UPP_XD15,
|
|
PINFUNC_LIST_TERMINATE
|
|
};
|
|
|
|
// Pin Mux functions for XDATA[7:0]
|
|
tePinFunc laPinFuncXData7_0[] =
|
|
{
|
|
UPP_XD0,
|
|
UPP_XD1,
|
|
UPP_XD2,
|
|
UPP_XD3,
|
|
UPP_XD4,
|
|
UPP_XD5,
|
|
UPP_XD6,
|
|
UPP_XD7,
|
|
PINFUNC_LIST_TERMINATE
|
|
};
|
|
|
|
// MbxA attributes //TODO: non-default attributes?
|
|
MBX_Attrs lsMbxAttrsA = MBX_ATTRS;
|
|
// MbxB attributes //TODO: non-default attributes?
|
|
MBX_Attrs lsMbxAttrsB = MBX_ATTRS;
|
|
|
|
// Length of Chan A MBXs
|
|
uint32_t lnMbxLenA = apDspUppConfig->nMbxLenA;
|
|
// Length of Chan B MBXs
|
|
uint32_t lnMbxLenB = apDspUppConfig->nMbxLenB;
|
|
|
|
|
|
// Configuration sanity checks
|
|
if (eeDisabled == apDspUppConfig->eChanADir &&
|
|
eeDisabled == apDspUppConfig->eChanBDir)
|
|
return -1;
|
|
|
|
if (apDspUppConfig->nHWInterruptLevel > 15 ||
|
|
apDspUppConfig->nHWInterruptLevel < 4)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// TODO: Additional checks?
|
|
|
|
|
|
// Check if we've already been initialized
|
|
if (false == mbFirstInit)
|
|
{
|
|
// TODO: any necessary shutdown before the re-init
|
|
|
|
// Reset all pin config to default? (need to? Make sense?)
|
|
}
|
|
|
|
mbFirstInit = false;
|
|
|
|
// Reset channels to disabled in case there is a failure
|
|
meChanADir = eeDisabled;
|
|
meChanADir = eeDisabled;
|
|
|
|
// Set uPP DMA Master Priority
|
|
tcDspSyscfg::SetMasterPriority(tcDspSyscfg::eeUPP,
|
|
apDspUppConfig->nDmaMasterPriority);
|
|
|
|
// Apply the appropriate pin mux settings to enable the uPP
|
|
// (based on configuration)
|
|
if (eeTransmit == apDspUppConfig->eChanADir ||
|
|
eeTransmit == apDspUppConfig->eChanBDir)
|
|
{
|
|
// Select the appropriate Transmit Clock
|
|
if (eeUPP_2xTXCLK == apDspUppConfig->eTxClockSel)
|
|
{
|
|
tcDspSyscfg::SetChipConfig(UPP_TX_CLKSRC_2xTXCLK);
|
|
|
|
// Enable 2xTXCLK pin
|
|
if (tcDspSyscfg::SetPinMuxConfig(UPP_2xTXCLK) < 0)
|
|
return -1;
|
|
}
|
|
else if (eePLL0_SYSCLK2 == apDspUppConfig->eTxClockSel)
|
|
{
|
|
tcDspSyscfg::SetChipConfig(ASYNC3_CLKSRC_PLL0_SYSCLK2);
|
|
tcDspSyscfg::SetChipConfig(UPP_TX_CLKSRC_ASYNC3);
|
|
}
|
|
else if (eePLL1_SYSCLK2 == apDspUppConfig->eTxClockSel)
|
|
{
|
|
tcDspSyscfg::SetChipConfig(ASYNC3_CLKSRC_PLL1_SYSCLK2);
|
|
tcDspSyscfg::SetChipConfig(UPP_TX_CLKSRC_ASYNC3);
|
|
}
|
|
}
|
|
|
|
if (eeDisabled != apDspUppConfig->eChanADir)
|
|
{
|
|
// Enable Channel A pins if it is not disabled
|
|
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncCHA) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (eeDisabled != apDspUppConfig->eChanBDir)
|
|
{
|
|
// Enable Channel B pins if it is not disabled
|
|
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncCHB) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (eeDisabled == apDspUppConfig->eChanBDir &&
|
|
eeDisabled != apDspUppConfig->eChanADir)
|
|
{
|
|
// Enable lower 8 bits for Channel A
|
|
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncData7_0) < 0)
|
|
return -1;
|
|
|
|
if (ee8Bit != apDspUppConfig->eChanBitWidthA)
|
|
{
|
|
// Enable upper 8 bits for Channel A
|
|
if (true == apDspUppConfig->bChanAUseXData)
|
|
{
|
|
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncXData7_0) < 0)
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncData15_8) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (eeDisabled != apDspUppConfig->eChanADir)
|
|
{
|
|
// Enable lower 8 bits for Channel A
|
|
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncData7_0) < 0)
|
|
return -1;
|
|
|
|
if (ee8Bit != apDspUppConfig->eChanBitWidthA)
|
|
{
|
|
// Enable upper 8 bits for Channel A
|
|
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncXData7_0) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (eeDisabled != apDspUppConfig->eChanBDir)
|
|
{
|
|
// Enable lower 8 bits for Channel B
|
|
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncData15_8) < 0)
|
|
return -1;
|
|
|
|
if (ee8Bit != apDspUppConfig->eChanBitWidthB)
|
|
{
|
|
// Enable upper 8 bits for Channel B
|
|
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncXData15_8) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// make sure to enable the power and clocks to the uPP device
|
|
tcDspLpsc::ConfigPeripheral(tcDspLpsc::eeUPP, tcDspLpsc::eeENABLE);
|
|
|
|
// Delete Chan A MBXs if they exists
|
|
if (NULL != mhMbxDoneA)
|
|
MBX_delete(mhMbxDoneA);
|
|
|
|
if (NULL != mhMbxIntA)
|
|
MBX_delete(mhMbxIntA);
|
|
|
|
if (NULL != mhMbxQueueA)
|
|
MBX_delete(mhMbxQueueA);
|
|
|
|
// Delete Chan B MBXs if they exists
|
|
if (NULL != mhMbxDoneB)
|
|
MBX_delete(mhMbxDoneB);
|
|
|
|
if (NULL != mhMbxIntB)
|
|
MBX_delete(mhMbxIntB);
|
|
|
|
if (NULL != mhMbxQueueB)
|
|
MBX_delete(mhMbxQueueB);
|
|
|
|
|
|
// Initialize Chan A MBXs if Chan A is enabled
|
|
if (eeDisabled != apDspUppConfig->eChanADir)
|
|
{
|
|
lsMbxAttrsA.name = "mhMbxDoneA";
|
|
mhMbxDoneA = MBX_create(sizeof(tsMbxMsg), lnMbxLenA,
|
|
&lsMbxAttrsA);
|
|
if (NULL == mhMbxDoneA)
|
|
return -1;
|
|
|
|
lsMbxAttrsA.name = "mhMbxIntA";
|
|
mhMbxIntA = MBX_create(sizeof(tsMbxMsg), 2, &lsMbxAttrsA);
|
|
if (NULL == mhMbxIntA)
|
|
return -1;
|
|
|
|
lsMbxAttrsA.name = "mhMbxQueueA";
|
|
mhMbxQueueA = MBX_create(sizeof(tsMbxMsg), lnMbxLenA,
|
|
&lsMbxAttrsA);
|
|
if (NULL == mhMbxQueueA)
|
|
return -1;
|
|
}
|
|
|
|
// Initialize Chan B MBXs if Chan B is enabled
|
|
if (eeDisabled != apDspUppConfig->eChanBDir)
|
|
{
|
|
lsMbxAttrsB.name = "mhMbxDoneB";
|
|
mhMbxDoneB = MBX_create(sizeof(tsMbxMsg), lnMbxLenB,
|
|
&lsMbxAttrsB);
|
|
if (NULL == mhMbxDoneB)
|
|
return -1;
|
|
|
|
lsMbxAttrsB.name = "mhMbxIntB";
|
|
mhMbxIntB = MBX_create(sizeof(tsMbxMsg), 2, &lsMbxAttrsB);
|
|
if (NULL == mhMbxIntB)
|
|
return -1;
|
|
|
|
lsMbxAttrsB.name = "mhMbxQueueB";
|
|
mhMbxQueueB = MBX_create(sizeof(tsMbxMsg), lnMbxLenB,
|
|
&lsMbxAttrsB);
|
|
if (NULL == mhMbxQueueB)
|
|
return -1;
|
|
}
|
|
|
|
// Reset the uPP
|
|
reset();
|
|
|
|
// Program UPCTL reg (mode, data width/format, etc.)
|
|
if (eeTransmit == apDspUppConfig->eChanADir &&
|
|
eeReceive == apDspUppConfig->eChanBDir)
|
|
{
|
|
luUpctlReg.sRegBits.MODE = eeAXmitBRcv; // Xmit/Rcv Mode
|
|
}
|
|
else if (eeReceive == apDspUppConfig->eChanADir &&
|
|
eeTransmit == apDspUppConfig->eChanBDir)
|
|
{
|
|
luUpctlReg.sRegBits.MODE = eeARcvBXmit; // Xmit/Rcv Mode
|
|
}
|
|
else if (eeTransmit == apDspUppConfig->eChanADir ||
|
|
eeTransmit == apDspUppConfig->eChanBDir)
|
|
{
|
|
luUpctlReg.sRegBits.MODE = eeAllXmit; // Xmit/Rcv Mode
|
|
}
|
|
else if (eeReceive == apDspUppConfig->eChanADir ||
|
|
eeReceive == apDspUppConfig->eChanBDir)
|
|
{
|
|
luUpctlReg.sRegBits.MODE = eeAllRcv; // Xmit/Rcv Mode
|
|
}
|
|
|
|
if (eeDisabled != apDspUppConfig->eChanBDir ||
|
|
true == apDspUppConfig->bChanAUseXData)
|
|
{
|
|
// Must "enable" both channels if B is active
|
|
// Though if it's just B active, we do not enable A's pinmuxing
|
|
// Or in case where only Channel A is active, but we want CHN=1
|
|
// data bit assignments
|
|
luUpctlReg.sRegBits.CHN = 1; // Only Chan A active, or Chan A/B active
|
|
}
|
|
|
|
luUpctlReg.sRegBits.SDRTXIL = 0; // Not supported... yet
|
|
luUpctlReg.sRegBits.DDRDEMUX = 0; // Not supported... yet
|
|
|
|
luUpctlReg.sRegBits.DRA = 0; // Chan A single/double data rate
|
|
|
|
if (ee8Bit != apDspUppConfig->eChanBitWidthA)
|
|
{
|
|
luUpctlReg.sRegBits.IWA = 1; // Chan A 8/16-bit interface
|
|
}
|
|
|
|
// Mod 8 because 8 and 16 bit = 0
|
|
luUpctlReg.sRegBits.DPWA = (apDspUppConfig->eChanBitWidthA)%8;
|
|
luUpctlReg.sRegBits.DPFA = eeRJSE; // Chan A data packing format
|
|
|
|
luUpctlReg.sRegBits.DRB = 0; // Chan B single/double data rate
|
|
|
|
if (ee8Bit != apDspUppConfig->eChanBitWidthB)
|
|
{
|
|
luUpctlReg.sRegBits.IWB = 1; // Chan B 8/16-bit interface
|
|
}
|
|
|
|
// Mod 8 because 8 and 16 bit = 0
|
|
luUpctlReg.sRegBits.DPWB = (apDspUppConfig->eChanBitWidthB)%8;
|
|
luUpctlReg.sRegBits.DPFB = eeRJSE; // Chan B data packing format
|
|
mpUppRegs->UPCTL = luUpctlReg.nRegWord;
|
|
|
|
// Program UPICR reg (signal enable, clock rate)
|
|
luUpicrReg.sRegBits.STARTPOLA = 0;
|
|
luUpicrReg.sRegBits.ENAPOLA = 0;
|
|
luUpicrReg.sRegBits.WAITPOLA = 0;
|
|
luUpicrReg.sRegBits.STARTA = apDspUppConfig->bChanAUseStart;
|
|
luUpicrReg.sRegBits.ENAA = 1;
|
|
luUpicrReg.sRegBits.WAITA = 1;
|
|
luUpicrReg.sRegBits.CLKDIVA = apDspUppConfig->nChanAClkDiv;
|
|
luUpicrReg.sRegBits.CLKINVA = 0;
|
|
luUpicrReg.sRegBits.TRISA = 0; // Chan A high-impedence state
|
|
|
|
luUpicrReg.sRegBits.STARTPOLB = 0;
|
|
luUpicrReg.sRegBits.ENAPOLB = 0;
|
|
luUpicrReg.sRegBits.WAITPOLB = 0;
|
|
luUpicrReg.sRegBits.STARTB = apDspUppConfig->bChanBUseStart;
|
|
luUpicrReg.sRegBits.ENAB = 1;
|
|
luUpicrReg.sRegBits.WAITB = 1;
|
|
luUpicrReg.sRegBits.CLKDIVB = apDspUppConfig->nChanBClkDiv;
|
|
luUpicrReg.sRegBits.CLKINVB = 0;
|
|
luUpicrReg.sRegBits.TRISB = 0; // Chan B high-impedence state
|
|
|
|
mpUppRegs->UPICR = luUpicrReg.nRegWord;
|
|
|
|
// Program UPIVR reg (idle xmit value)
|
|
luUpivrReg.sRegBits.VALA = 0xFFFF; // Chan A idle value, if TRISA==0
|
|
luUpivrReg.sRegBits.VALB = 0xFFFF; // Chan B idle value, if TRISB==0
|
|
|
|
mpUppRegs->UPIVR = luUpivrReg.nRegWord;
|
|
|
|
// Program UPTCR reg (i/o threshold)
|
|
luUptcrReg.sRegBits.RDSIZEI = apDspUppConfig->eThresholdRxA;
|
|
luUptcrReg.sRegBits.RDSIZEQ = apDspUppConfig->eThresholdRxB;
|
|
|
|
luUptcrReg.sRegBits.TXSIZEA = apDspUppConfig->eThresholdTxA;
|
|
luUptcrReg.sRegBits.TXSIZEB = apDspUppConfig->eThresholdTxB;
|
|
|
|
mpUppRegs->UPTCR = luUptcrReg.nRegWord;
|
|
|
|
// Program UPDLB reg (digital loopback)
|
|
mpUppRegs->UPDLB = 0; // TODO: support internal loopback?
|
|
|
|
// Clear all interrupts using UPIEC
|
|
mpUppRegs->UPIEC = 0x1F1F;
|
|
|
|
// Program uPP interrupt enable reg (UPIES)
|
|
luUpiesReg.nRegWord = 0;
|
|
// DMA I interrupts
|
|
if (eeDisabled != apDspUppConfig->eChanADir)
|
|
{
|
|
luUpiesReg.sRegBits.EOWI = 1;
|
|
luUpiesReg.sRegBits.DPEI = 1;
|
|
luUpiesReg.sRegBits.UORI = 1;
|
|
luUpiesReg.sRegBits.ERRI = 1;
|
|
// No need to service end of line interrupt
|
|
luUpiesReg.sRegBits.EOLI = 0;
|
|
}
|
|
// DMA Q interrupts
|
|
if (eeDisabled != apDspUppConfig->eChanBDir)
|
|
{
|
|
luUpiesReg.sRegBits.EOWQ = 1;
|
|
luUpiesReg.sRegBits.DPEQ = 1;
|
|
luUpiesReg.sRegBits.UORQ = 1;
|
|
luUpiesReg.sRegBits.ERRQ = 1;
|
|
// No need to service end of line interrupt
|
|
luUpiesReg.sRegBits.EOLQ = 0;
|
|
}
|
|
|
|
mpUppRegs->UPIES = luUpiesReg.nRegWord;
|
|
|
|
|
|
// Register ISR (if enabled)
|
|
// Setup interrupt handling function
|
|
// TODO: Failure codes for these functions?
|
|
HWI_dispatchPlug(apDspUppConfig->nHWInterruptLevel,
|
|
(Fxn)isr,
|
|
-1,
|
|
&hwi_attrs);
|
|
HWI_eventMap(apDspUppConfig->nHWInterruptLevel,
|
|
94);
|
|
C62_enableIER(1 << apDspUppConfig->nHWInterruptLevel);
|
|
|
|
|
|
// Store directionality of channels
|
|
meChanADir = apDspUppConfig->eChanADir;
|
|
meChanBDir = apDspUppConfig->eChanBDir;
|
|
|
|
// Turn on the uPP and other final UPPCR config
|
|
luUppcrReg.sRegBits.FREE = 1; // Emulation will not halt uPP
|
|
luUppcrReg.sRegBits.EN = 1; // Enable uPP device
|
|
mpUppRegs->UPPCR = luUppcrReg.nRegWord;
|
|
|
|
// Start the Chan A thread for handling the DMA
|
|
if (eeDisabled != apDspUppConfig->eChanADir)
|
|
{
|
|
if (NULL != mhDmaTskA)
|
|
TSK_delete(mhDmaTskA);
|
|
|
|
tsk_attrs = TSK_ATTRS;
|
|
tsk_attrs.name = "DmaTskA";
|
|
tsk_attrs.stacksize = 1024;
|
|
tsk_attrs.priority = apDspUppConfig->nTskPriorityChanA;
|
|
mhDmaTskA = TSK_create((Fxn)programDMA, &tsk_attrs, this,
|
|
eeChanA);
|
|
|
|
// Check that task creation was successful
|
|
if (NULL == mhDmaTskA)
|
|
return -1;
|
|
}
|
|
|
|
// Start the Chan B thread for handling the DMA
|
|
if (eeDisabled != apDspUppConfig->eChanBDir)
|
|
{
|
|
if (NULL != mhDmaTskB)
|
|
TSK_delete(mhDmaTskB);
|
|
|
|
tsk_attrs = TSK_ATTRS;
|
|
tsk_attrs.name = "DmaTskB";
|
|
tsk_attrs.stacksize = 1024;
|
|
tsk_attrs.priority = apDspUppConfig->nTskPriorityChanB;
|
|
mhDmaTskB = TSK_create((Fxn)programDMA, &tsk_attrs, this,
|
|
eeChanB);
|
|
|
|
// Check that task creation was successful
|
|
if (NULL == mhDmaTskB)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Perform software reset of the uPP.
|
|
*
|
|
* @return None.
|
|
*/
|
|
void
|
|
tcDspUpp::reset()
|
|
{
|
|
tuUppcrReg luUppcrReg = {0};
|
|
|
|
// Read current contents of the register
|
|
luUppcrReg.nRegWord = mpUppRegs->UPPCR;
|
|
|
|
// Place the uPP in SW reset
|
|
luUppcrReg.sRegBits.SWRST = 1; // SW reset enabled
|
|
mpUppRegs->UPPCR = luUppcrReg.nRegWord;
|
|
|
|
// Wait at least 200 cycles
|
|
TSK_sleep(200);
|
|
|
|
// Clear the SW reset bit
|
|
luUppcrReg.sRegBits.SWRST = 0; // SW reset disabled
|
|
mpUppRegs->UPPCR = luUppcrReg.nRegWord;
|
|
}
|
|
|
|
/**
|
|
* Get handle to mailbox for associated channel where info on
|
|
*/
|
|
MBX_Handle
|
|
tcDspUpp::getMBX(teUppChan aeChan)
|
|
{
|
|
return (eeChanB == aeChan)?mhMbxDoneB:mhMbxDoneA;
|
|
}
|
|
|
|
/**
|
|
* Queue transmit of given data buffer. Use getMBX() to get corresponding
|
|
* mailbox where pointer info will be posted once data has been tramsmitted.
|
|
*/
|
|
int
|
|
tcDspUpp::transmit(teUppChan aeChan,
|
|
const uint8_t* apXmitData,
|
|
uint16_t anByteCnt,
|
|
uint16_t anLineCnt,
|
|
uint16_t anLineOffset)
|
|
{
|
|
// MBX Queue msg
|
|
tsMbxMsg lsMbxMsg;
|
|
// Queue MBX
|
|
MBX_Handle lhMbxQueue = (eeChanB == aeChan)?mhMbxQueueB:mhMbxQueueA;
|
|
|
|
// Check aeChan directionality...
|
|
if (eeChanA == aeChan)
|
|
{
|
|
if (eeTransmit != meChanADir)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else if (eeChanB == aeChan)
|
|
{
|
|
if (eeTransmit != meChanBDir)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// Check if apXmitData is on 64-bit aligned
|
|
if (0 != (((uint32_t)apXmitData)&0x7))
|
|
return -1;
|
|
|
|
// Check that anByteCnt is even
|
|
if (0 != (anByteCnt&0x1))
|
|
return -1;
|
|
|
|
// Check that anLineOffset is 64-bit aligned
|
|
if (0 != (anLineOffset&0x7))
|
|
return -1;
|
|
|
|
//TODO: Check restrictions on other inputs
|
|
|
|
// Setup the request
|
|
lsMbxMsg.pBufPtr = (uint8_t*)apXmitData;
|
|
lsMbxMsg.nByteCnt = anByteCnt;
|
|
lsMbxMsg.nLineCnt = anLineCnt;
|
|
lsMbxMsg.nLineOffset = anLineOffset;
|
|
lsMbxMsg.pOptArg = NULL;
|
|
|
|
// Add the request to the queue mailbox
|
|
if (false == MBX_post(lhMbxQueue, &lsMbxMsg, SYS_FOREVER))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
// TODO: failure conditions!
|
|
}
|
|
|
|
/**
|
|
* Add buffer to receive queue. Use getMBX() to get corresponding
|
|
* mailbox where pointer info will be posted once data has been received.
|
|
*/
|
|
int
|
|
tcDspUpp::receive(teUppChan aeChan,
|
|
uint8_t* apRcvData,
|
|
uint16_t anByteCnt,
|
|
uint16_t anLineCnt,
|
|
uint16_t anLineOffset)
|
|
{
|
|
// MBX Queue msg
|
|
tsMbxMsg lsMbxMsg;
|
|
// Queue MBX
|
|
MBX_Handle lhMbxQueue = (eeChanB == aeChan)?mhMbxQueueB:mhMbxQueueA;
|
|
|
|
// Check aeChan directionality...
|
|
if (eeChanA == aeChan)
|
|
{
|
|
if (eeReceive != meChanADir)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else if (eeChanB == aeChan)
|
|
{
|
|
if (eeReceive != meChanBDir)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// Check if apXmitData is on 64-bit aligned
|
|
if (0 != (((uint32_t)apRcvData)&0x7))
|
|
return -1;
|
|
|
|
// Check that anByteCnt is even
|
|
if (0 != (anByteCnt&0x1))
|
|
return -1;
|
|
|
|
// Check that anLineOffset is 64-bit aligned
|
|
if (0 != (anLineOffset&0x7))
|
|
return -1;
|
|
|
|
|
|
// Setup the request
|
|
lsMbxMsg.pBufPtr = apRcvData;
|
|
lsMbxMsg.nByteCnt = anByteCnt;
|
|
lsMbxMsg.nLineCnt = anLineCnt;
|
|
lsMbxMsg.nLineOffset = anLineOffset;
|
|
lsMbxMsg.pOptArg = NULL;
|
|
|
|
// Add the request to the queue mailbox
|
|
if (false == MBX_post(lhMbxQueue, &lsMbxMsg, SYS_FOREVER))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
//TODO: failure conditions!
|
|
}
|
|
|
|
/**
|
|
* Thread for programming the DMA for the specified channel.
|
|
*/
|
|
void
|
|
tcDspUpp::programDMA(tcDspUpp* apDspUpp, teUppChan aeChan)
|
|
{
|
|
// Pointer to the tcDspUpp object
|
|
tcDspUpp* lpDspUpp = apDspUpp;
|
|
// Queue MBX
|
|
MBX_Handle lhMbxQueue = (eeChanB == aeChan)?
|
|
lpDspUpp->mhMbxQueueB:lpDspUpp->mhMbxQueueA;
|
|
// Intermediate MBX (for buffers being DMAed)
|
|
MBX_Handle lhMbxInt = (eeChanB == aeChan)?
|
|
lpDspUpp->mhMbxIntB:lpDspUpp->mhMbxIntA;
|
|
// Local MBX message for copying and setting DMA
|
|
tsMbxMsg lsMbxMsg;
|
|
// Used for checking DMA status
|
|
tuUpiqs2Reg luUpiqs2Reg = {0};
|
|
// Used for setting the DMA reg 0
|
|
tuUpiqd1Reg luUpiqd1Reg = {0};
|
|
|
|
// This thread runs continuously
|
|
while(1)
|
|
{
|
|
|
|
// Pend on mhMbxQueue waiting for a new message that can be
|
|
// used to program the DMA
|
|
MBX_pend(lhMbxQueue, &lsMbxMsg, SYS_FOREVER);
|
|
|
|
// Post to mhMbxInt waiting for space to open up in DMA
|
|
MBX_post(lhMbxInt, &lsMbxMsg, SYS_FOREVER);
|
|
|
|
// Now we should be able to safely program the DMA
|
|
|
|
// Read the appropriate DMA status register
|
|
if (eeChanB == aeChan)
|
|
{
|
|
luUpiqs2Reg.nRegWord = lpDspUpp->mpUppRegs->UPQS2;
|
|
}
|
|
else
|
|
{
|
|
luUpiqs2Reg.nRegWord = lpDspUpp->mpUppRegs->UPIS2;
|
|
}
|
|
|
|
// Check if the DMA can be programmed
|
|
while (1 == luUpiqs2Reg.sRegBits.PEND)
|
|
{
|
|
// The DMA is busy, this should not happen
|
|
if (NULL != lpDspUpp->mpErrorCallback)
|
|
lpDspUpp->mpErrorCallback(0xDEADBEEF);
|
|
|
|
// Maybe it will fix itself?
|
|
TSK_sleep(100);
|
|
}
|
|
|
|
// Program the DMA
|
|
luUpiqd1Reg.sRegBits.BCNT = lsMbxMsg.nByteCnt;
|
|
luUpiqd1Reg.sRegBits.LNCNT = lsMbxMsg.nLineCnt;
|
|
|
|
if (eeChanB == aeChan)
|
|
{
|
|
lpDspUpp->mpUppRegs->UPQD0 = (uint32_t)lsMbxMsg.pBufPtr;
|
|
lpDspUpp->mpUppRegs->UPQD1 = luUpiqd1Reg.nRegWord;
|
|
lpDspUpp->mpUppRegs->UPQD2 = lsMbxMsg.nLineOffset;
|
|
}
|
|
else
|
|
{
|
|
lpDspUpp->mpUppRegs->UPID0 = (uint32_t)lsMbxMsg.pBufPtr;
|
|
lpDspUpp->mpUppRegs->UPID1 = luUpiqd1Reg.nRegWord;
|
|
lpDspUpp->mpUppRegs->UPID2 = lsMbxMsg.nLineOffset;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle any uPP related interrupts that might occur.
|
|
*
|
|
* \return 0 on success, negative on failure.
|
|
*/
|
|
int
|
|
tcDspUpp::isr(tcDspUpp* apDspUpp)
|
|
{
|
|
// Return value
|
|
int retval = 0;
|
|
// Pointer to the tcDspUpp object
|
|
tcDspUpp* lpDspUpp = apDspUpp;
|
|
// Local copy of uPP registers so that we don't have to
|
|
// dereference lpDspUpp so many times
|
|
volatile tsUppRegs* const lpUppRegs = lpDspUpp->mpUppRegs;
|
|
// Value of the UPIER register, which tells us which interrupts occurred
|
|
tuUpierReg luUpierReg = {0};
|
|
// Used to clear the UPIER register interrupts once processed
|
|
tuUpierReg luUpierRegClr = {0};
|
|
// Used to update the Done MBX
|
|
tsMbxMsg lsMbxMsg;
|
|
|
|
// Check for interrupts
|
|
luUpierReg.nRegWord = lpUppRegs->UPIER;
|
|
|
|
// Process all pending interrupts.
|
|
while (0 != luUpierReg.nRegWord)
|
|
{
|
|
// Check for Channel I programming error interrupt
|
|
if (luUpierReg.sRegBits.DPEI == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.nRegWord = 0;
|
|
luUpierRegClr.sRegBits.DPEI = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
|
|
// Handle the interrupt
|
|
if (NULL != lpDspUpp->mpErrorCallback)
|
|
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
|
|
}
|
|
|
|
// Check for Channel I underrun/overflow interrupt
|
|
if (luUpierReg.sRegBits.UORI == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.nRegWord = 0;
|
|
luUpierRegClr.sRegBits.UORI = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
|
|
// Handle the interrupt
|
|
if (NULL != lpDspUpp->mpErrorCallback)
|
|
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
|
|
}
|
|
|
|
// Check for Channel I error interrupt
|
|
if (luUpierReg.sRegBits.ERRI == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.nRegWord = 0;
|
|
luUpierRegClr.sRegBits.ERRI = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
|
|
// Handle the interrupt
|
|
if (NULL != lpDspUpp->mpErrorCallback)
|
|
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
|
|
}
|
|
|
|
// Check for Channel I End-of-Window interrupt
|
|
if (luUpierReg.sRegBits.EOWI == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.nRegWord = 0;
|
|
luUpierRegClr.sRegBits.EOWI = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
|
|
// Handle the interrupt
|
|
|
|
// Get the DMAed data info
|
|
if (true == MBX_pend(lpDspUpp->mhMbxIntA,
|
|
&lsMbxMsg,
|
|
0))
|
|
{
|
|
// Update the done MBX
|
|
if (false == MBX_post(lpDspUpp->mhMbxDoneA,
|
|
&lsMbxMsg,
|
|
0))
|
|
{
|
|
// Return queue overflow!
|
|
retval = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No data in the intermediate mailbox, but we received
|
|
// an interrupt, this is a problem
|
|
retval = -1;
|
|
}
|
|
}
|
|
|
|
// Check for Channel I End-of-Line interrupt
|
|
if (luUpierReg.sRegBits.EOLI == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.sRegBits.EOLI = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
luUpierRegClr.nRegWord = 0;
|
|
|
|
// Handle the interrupt
|
|
|
|
}
|
|
|
|
// Check for Channel Q programming error interrupt
|
|
if (luUpierReg.sRegBits.DPEQ == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.nRegWord = 0;
|
|
luUpierRegClr.sRegBits.DPEQ = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
|
|
// Handle the interrupt
|
|
if (NULL != lpDspUpp->mpErrorCallback)
|
|
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
|
|
}
|
|
|
|
// Check for Channel Q underrun/overflow interrupt
|
|
if (luUpierReg.sRegBits.UORQ == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.nRegWord = 0;
|
|
luUpierRegClr.sRegBits.UORQ = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
|
|
// Handle the interrupt
|
|
if (NULL != lpDspUpp->mpErrorCallback)
|
|
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
|
|
}
|
|
|
|
// Check for Channel Q error interrupt
|
|
if (luUpierReg.sRegBits.ERRQ == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.nRegWord = 0;
|
|
luUpierRegClr.sRegBits.ERRQ = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
|
|
// Handle the interrupt
|
|
if (NULL != lpDspUpp->mpErrorCallback)
|
|
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
|
|
}
|
|
|
|
// Check for Channel Q End-of-Window interrupt
|
|
if (luUpierReg.sRegBits.EOWQ == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.nRegWord = 0;
|
|
luUpierRegClr.sRegBits.EOWQ = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
|
|
// Handle the interrupt
|
|
|
|
// Get the DMAed data info
|
|
if (true == MBX_pend(lpDspUpp->mhMbxIntB,
|
|
&lsMbxMsg,
|
|
0))
|
|
{
|
|
// Update the done MBX
|
|
if (false == MBX_post(lpDspUpp->mhMbxDoneB,
|
|
&lsMbxMsg,
|
|
0))
|
|
{
|
|
// Return queue overflow!
|
|
retval = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No data in the intermediate mailbox, but we received
|
|
// an interrupt, this is a problem
|
|
retval = -1;
|
|
}
|
|
}
|
|
|
|
// Check for Channel Q End-of-Line interrupt
|
|
if (luUpierReg.sRegBits.EOLQ == 1)
|
|
{
|
|
// Clear the interrupt
|
|
luUpierRegClr.nRegWord = 0;
|
|
luUpierRegClr.sRegBits.EOLQ = 1;
|
|
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
|
|
|
|
// Handle the interrupt
|
|
|
|
}
|
|
|
|
// Check for more interrupts
|
|
luUpierReg.nRegWord = lpUppRegs->UPIER;
|
|
}
|
|
|
|
// Write end of interrupt vector to allow future calls
|
|
lpUppRegs->UPEOI = 0;
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* Set the error callback function.
|
|
*/
|
|
void
|
|
tcDspUpp::registerErrorCallback(tfErrorCallback afErrorCallback)
|
|
{
|
|
mpErrorCallback = afErrorCallback;
|
|
}
|
|
|
|
/**
|
|
* Private constructor.
|
|
*/
|
|
tcDspUpp::tcDspUpp()
|
|
: mpUppRegs((tsUppRegs*)UPP_REG_BASE)
|
|
, mhDmaTskA(NULL)
|
|
, mhDmaTskB(NULL)
|
|
, mhMbxDoneA(NULL)
|
|
, mhMbxDoneB(NULL)
|
|
, mhMbxIntA(NULL)
|
|
, mhMbxIntB(NULL)
|
|
, mhMbxQueueA(NULL)
|
|
, mhMbxQueueB(NULL)
|
|
, mpErrorCallback(NULL)
|
|
, mbFirstInit(true)
|
|
, meChanADir(eeDisabled)
|
|
, meChanBDir(eeDisabled)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* Private destructor.
|
|
*/
|
|
tcDspUpp::~tcDspUpp()
|
|
{
|
|
|
|
}
|
|
|