This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MPY32: 24x16 (Q1.23*Q1.15) multiplier?



Hi,

 

I have two fractional numbers, one is in Q1.23 format and the other is in Q1.15 format. I want to multiply this two numbers and the result should be again in Q1.23.

unsigned char A[3]; // 24 bit, Q1.23

unsigned int B; // 16 bit Q1.15

unsigned long result;

 

Does MPY32 module support this operation? Because the code below is not working.

MPY32CTL0 = MPYFRAC + MPYSAT;

MPYS32L = (unsigned int)A[1]  << 8 | A[0];

MPYS32H_B = A[2];

OP2 = B;

result = (unsigned long)RES2 << 16 | RES1;

 

Regards,

BP.

  • Looks like you are too eager to get the result.

    RES1 is available 5 MCLK cycles after writing, RES2 takes 6 cycles. Also, RES2 is the first one you access in your code. It is simply not ready when you read it.

    Normally, the first result you fetch is RES0, which is available after 3 MCLK cycles. And since a move from memory to register takes at least 3 cycles, all is well. Normally.
    When using indirect or indirect autoincrement addressing is used for reading the result registers (manual optimisation), it will only take 2 cycles and there's need to put a NOP in between.

    Put three NOPs ( __no_operation() )between writing OP2 and the result espression and things should work (provided that the formula/ the use of MPYFRAC itself is correct, which I didn't check). If RESO1 is the first result register that is accessed by the compiler, two NOPs are enough, but better safe than sorry here.

  • Hi,

     

    I think the problem is different. When ADC.I0[0] = -1136645 and ADC.V0[0] = -5940, expected result is -56099, but power_vi0() function returns 206044.

     

     

     

    signed long power_vi0(void)

    {

        MPY32CTL0 = MPYFRAC + MPYSAT;

     

        MPYS32L = ADC.I0[0];    // ADC.I0[0] is in Q1.23 format

        MPYS32H_B = (ADC.I0[0] >> 16) & 0xFF;

       

        OP2 = ADC.V0[0];    // ADC.V0[0] is in Q1.15 format

       

        __no_operation();

        __no_operation();

        __no_operation();

        __no_operation();

        __no_operation();

       

        return((unsigned long)RES2 << 16 | RES1); // the result again in Q1.23

    }

  • From the manual:

    "Reading the result register RES1 gives the result as 16-bit Q15 number. The 32-bit Q31 result of a multiplication of two 32-bit Q31 numbers is accessed by reading registers RES2 and RES3."

    So I wonder what you are reading at all. First, you're reading RES1, which is, according to the docs, the (saturated) Q1.15 result, then you move the lower 16 bits of the Q1.31 result to the upper 16 bits of the return value, reaulting in, well, I don't know what. At least no Q1.23.

    IMHO (but I may be wrong, as I never dealt with fractional number and even less with mixed formates), you should for a Q1.23 result use the upper 24 bits of the Q1.31 result. Which is the Q1.31 value shifted right by 8:

    return ( RES2>>8 | (unsigned long) RES3 <<8);

  • BasePointer said:
    I want to round up this multiplication result. Is there any easy way for this (fast, trick)?

    Round up to what? Q numbers are fractions of 1. So you want to know whether the result is greater or smaller than 0.5?
    This is simple:

    The bit behind the sign bit has the value of (2^n-1)(2^n) = 2^-1 = 0.5. So if the bit right of the sign bit is set, the value is equal or greater than 0.5. If the bit is clear, the value is less than 0.5.

    If you need to know whether the result is greater than 0.5 (but not equal), again chech the bit and then check whether any one of the remaining bits is set too.

    For x being the integer and y the fraction in Q1.15 format, you can do

    x += (y&0x4000?1:0)*(y&0x8000?-1:1);

    This also obeys the sign of the fraction (second part of the formula).

    You can further shrink and optimize it:

    x += (y&0x4000?(y&0x8000?-1:1):0);

     

  • Hi,

     

    For example, let say A is in Q1.15 format and B is in Q1.23 format.

    A = -5940 (means -5940 / 2^15 = -0.1812744140625)

    B = -1136645 (means -1136645 / 2^23 = -0.135498642921448)

     

    If we multiply this two real numbers: -0.1812744140625*-0.135498642921448 = 0.0245624371018494. This is the expected number. If we try to write the expected value in Q1.23 fotmat, we need 0.0245624371018494*2^23 = 206044.7 As an integer this number should be 206045, not 206044.

     

    For these A and B, my multiplication function in my previous message returns 206044. But I need 206045.

    Thanks.

  • BasePointer said:
    A = -5940 (means -5940 / 2^15 = -0.1812744140625)

    Well there's the first problem. A isn't -5940. A is a binary representation of -0.1812744140625. But it is NOT a decimal number. You simply cannot write it as decimal. You have to write it as 16 digit binary or hex value.
    While this may (accidentally) be right for a Q1.15, it will definitely lead to a totally wrong result when applied to a Q1.23 value. Since there is NO 24 bit integer in C.  If you insist in doing so, teh Q1.23 representation of -0.135498642921448 (your B) becomes +15640571, which is confusing.

    If you write 0xeea7fb (6 digits = 24 bits) things are much clearer.

    But back to the original problem. You want to round the Q1.23 result. That cannot be done form the Q1.32 result itself, as the information is lost.
    But you can do it while you still have the Q1.31 result as delivered from the MPY. Simply add 0x80 to the result (this is the value of the first bit that is dropped when moving from Q1.31 to Q1.23) and you'll adding 1/2 of the LSB of the Q1.23 result, which is a reasonable round-up. But at this point you'll have to check for an overflow  yourself (in case that the upper 23 bits (independently of the MSB) are all-1. If they are, it would result in a jump from +-0.999 to -+0, which is definitely not what you want.

**Attention** This is a public forum