Hello everyone !
I've recently hit a snag in a motor FOC project of mine: the CORDIC unit on a STM32G431xB doesn't give out similar q1.15 results when the input are 32 and 16 bits.
In the current setup, both sine an cosine coefficient are pre-stored as q1.15 values in two separate sensor input buffers (from hall resolvers).
In order to simulate actual q1.31 value, I simply left-shift the buffered q1.15 values by 16 upon use, and tolerates the LSB loss for the purposes of the tests.
Here's an excerpt of the test method :
[main loop]
float *pResA = resultsA, *pResB = resultsB; // For the purpose of the test, our q1.15 results will be converted to degrees as f32 values.
cnt = NB_ANGLE_SAMPLES;
CordicUnit::clearError();
while (cnt-- && !CordicUnit::hasError()) {
// Input sin/cos coeffs' are stored in q1.15 buffers, addressed through *pSin and *pCos.
// Both functions returns q1.15 results
*pResA++ = 180.0f + 180.0f * ((float)CordicUnit::atan2_i31_o15(int32_t(*pSin) << 16, (int32_t)(*pCos) << 16) / 32767); // fed truncated Q1.31 values.
*pResB++ = 180.0f + 180.0f * ((float)CordicUnit::atan2_i15_o15(*pSin, *pCos) / 32767); // fed the actual Q1.15 values.
pSin++;
pCos++;
}
[/main loop]
One would expect resultA and resultB contents to vary only by a few percents, however, that's not the case :
results from atan2_i31_o15
results of CORDIC Phase (atan2) with two Q1.31 iinputs
results from atan2_i15_o15
Results of CORDIC Phase (atan2) with two Q1.15 inputsWut ?
Here are the declarations & definitions of both, bare-metal CordicUnit::atan2xxxxx functions :
[declarations]
enum CordicFunction_e : uint32_t{
CORDIC_ATAN2_I31_O15 = (CORDIC_CSR_RESSIZE | CORDIC_NBWRITE_2 | (4 << CORDIC_CSR_PRECISION_Pos) | (2 << CORDIC_CSR_FUNC_Pos)),
CORDIC_ATAN2_I15_O15 = (CORDIC_CSR_ARGSIZE | CORDIC_CSR_RESSIZE | (4 << CORDIC_CSR_PRECISION_Pos) | (2 << CORDIC_CSR_FUNC_Pos)),
};
[/declarations]
[definitions]
int16_t CordicUnit::atan2_i31_o15(int32_t y, int32_t x)
{
if (_completedLast == false)
return 0;
CORDIC->CSR = CordicFunction_e::CORDIC_ATAN2_I31_O15;
CORDIC->WDATA = (uint32_t)x;
CORDIC->WDATA = (uint32_t)y;
_completedLast = false;
uint32_t start = micros(); // timeout timestamp
// Wait for completion, with some ample timeout (500µs)
while ((CORDIC->CSR & CORDIC_CSR_RRDY) == 0 && ((micros() - start) < 500))
asm(" nop");
// Timed out ?
if ((CORDIC->CSR & CORDIC_CSR_RRDY) == 0) {
_cordicHasErrors = true;
return 0;
}
_completedLast = true;
return (CORDIC->RDATA & 0xFFFFU); // discards the MSW and its modulus result
}
int16_t CordicUnit::atan2_i15_o15(int16_t y, int16_t x)
{
if (_completedLast == false)
return 0;
CORDIC->CSR = CordicFunction_e::CORDIC_ATAN2_I15_O15;
CORDIC->WDATA = (((uint32_t)y) << 16) | (uint32_t)x;
_completedLast = false;
uint32_t start = micros(); // timeout timestamp
// Wait for completion, with some ample timeout (500µs)
while ((CORDIC->CSR & CORDIC_CSR_RRDY) == 0 && ((micros() - start) < 500))
asm(" nop");
// Timed out ?
if ((CORDIC->CSR & CORDIC_CSR_RRDY) == 0) {
_cordicHasErrors = true;
return 0;
}
_completedLast = true;
return (CORDIC->RDATA & 0xFFFFU); // discards the MSW and its modulus result
}
[/definitions]
I'm quite at a loss here.
Using either q1.15 or q1.31 inputs isn't a costly time issue, but I cannot fathom why the results differ so much.
Any idea ?