dsp_math_multiply_sat explanation

Technical questions regarding the XTC tools and programming with XMOS.
User avatar
aclassifier
Respected Member
Posts: 510
Joined: Wed Apr 25, 2012 8:52 pm

Post by aclassifier »

Like this? See comment.

Code: Select all

static inline int sat31_int64__(long long ahl, int mant, unsigned &sat_cnt){
    asm volatile("#sat31_int64:");
    int res;
    int ah = ahl >> 32;
    unsigned al = ahl & 0xFFFFFFFF;
    int pos=mant;
    asm("lsats %0,%1,%2":
        "=r"(ah),"=r"(al):
        "r"(pos),"0"(ah),"1"(al));
    // ===
    res = (((long long)ah)<<32) | al;
    if (res != ahl) { // 64 bits compare, but since the return res is 32 bits, I'd like a 32 bits compare here as well..
        sat_cnt++;
    } else {}
    // ===
    asm("lextract %0,%1,%2,%3,32":
        "=r"(res):
        "r"(ah),"r"(al),"r"(pos));
    return res;
}
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
fabriceo
XCore Addict
Posts: 246
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

yes should work perfectly
User avatar
aclassifier
Respected Member
Posts: 510
Joined: Wed Apr 25, 2012 8:52 pm

Post by aclassifier »

Maybe not, since that long long ended up into int res.
Better, I'd assume:

Code: Select all

static inline int sat31_int64__(long long ahl, int mant, unsigned &sat_cnt){
    asm volatile("#sat31_int64:");
    int res;
    int ah = ahl >> 32;
    unsigned al = ahl & 0xFFFFFFFF;
    int pos=mant;
    asm("lsats %0,%1,%2":
        "=r"(ah),"=r"(al):
        "r"(pos),"0"(ah),"1"(al));
    // ===
    const long long ahl_sat_64 = (((long long)ah)<<32) | al;
    if (ahl_sat_64 != ahl) { // 64 bits compare, but since the return res is 32 bits, I'd like a 32 bits compare here as well..
        sat_cnt++;
    } else {}
    // ===
    asm("lextract %0,%1,%2,%3,32":
        "=r"(res):
        "r"(ah),"r"(al),"r"(pos));
    return res;
}
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
fabriceo
XCore Addict
Posts: 246
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

exact...
or you could copy ah and al into some r1, r0 and then compare them one by one after the lsats...
User avatar
aclassifier
Respected Member
Posts: 510
Joined: Wed Apr 25, 2012 8:52 pm

Post by aclassifier »

I have now made two additional versions of sat31_int64. I haven't looked into generated code, (well I have, but I don't understand the rather magic .build/mymath.s file (attached). Must have XCC_FLAGS += -save-temps for it to remain), and haven't tested time consumption, so I don't know which strategy is the best.

One uses unions and can then drop ah and al calculations and does two 32 bit compares:

Code: Select all

typedef struct {
    uint32_t lsb;
    uint32_t msb;
} parts64_t;

typedef struct {
    union {
        int64_t   int64_val;
        uint64_t  uint64_val;
        parts64_t parts64;
    } u;
} xy64_t;

static inline int32_t sat31_int64_C (const xy64_t ahl, const int mant, unsigned &sat_cnt) {
    asm volatile("#sat31_int64_C:");
    xy64_t ahl_64 = ahl;
    const int pos=mant;
    asm("lsats %0,%1,%2":
        "=r"(ahl_64.u.parts64.msb),"=r"(ahl_64.u.parts64.lsb):
        "r"(pos),"0"(ahl_64.u.parts64.msb),"1"(ahl_64.u.parts64.lsb));
    // ===
    if ((ahl_64.u.parts64.msb != ahl.u.parts64.msb) || (ahl_64.u.parts64.lsb != ahl.u.parts64.lsb)) {
        sat_cnt++;
    } else {}
    // ===
    asm("lextract %0,%1,%2,%3,32":
        "=r"(ahl_64.u.parts64.lsb):
        "r"(ahl_64.u.parts64.msb),"r"(ahl_64.u.parts64.lsb),"r"(pos));
    
    return ahl_64.u.parts64.lsb;
}
This has ah and al calculations but does one 64 bits compare:

Code: Select all


static inline int sat31_int64_B (const long long ahl, const int mant, unsigned &sat_cnt){
    asm volatile("#sat31_int64_B:");
    int res;
    int ah = ahl >> 32;
    unsigned al = ahl & 0xFFFFFFFF;
    int pos=mant;
    asm("lsats %0,%1,%2":
        "=r"(ah),"=r"(al):
        "r"(pos),"0"(ah),"1"(al));
    // ===
    const long long ahl_64 = (((long long)ah)<<32) | al;
    if (ahl_64 != ahl) { 
        sat_cnt++;
    } else {}
    // ===
    asm("lextract %0,%1,%2,%3,32":
        "=r"(res):
        "r"(ah),"r"(al),"r"(pos));
    return res;
}

Original code:

Code: Select all

static inline int sat31_int64(long long ahl, int mant){
    asm volatile("#sat31_int64:");
    int res;
    int ah = ahl >> 32;
    unsigned al = ahl & 0xFFFFFFFF;
    int pos=mant;
    asm("lsats %0,%1,%2":"=r"(ah),"=r"(al):"r"(pos),"0"(ah),"1"(al));
    asm("lextract %0,%1,%2,%3,32":"=r"(res):"r"(ah),"r"(al),"r"(pos));
    return res;
}
Here I run the code, and have inserted the prints, with explanations:

Code: Select all

static inline void mymath_test_B (const int mant, const unsigned shift_left_num, const int offset){
    /*                                               7654321076543210
    0: mant 28, shift_left_num 58, offset 0, ahl = 0x0400000000000000
    1: res = 0x40000000
    2: res = 0x40000000, sat_cnt = 0
    3: res = 0x40000000, sat_cnt = 0
                                                                               -----1--........--------........s-------........--------........ 
    0: mant 28, shift_left_num 59, offset -1, ahl = 0x07FFFFFFFFFFFFFF // pos  0000011111111111111111111111111111111111111111111111111111111111
    1: res = 0x7FFFFFFF                                                // >>28 0000000000000000000000000000000001111111111111111111111111111111 
    2: res = 0x7FFFFFFF, sat_cnt = 0
    3: res = 0x7FFFFFFF, sat_cnt = 0
                                                                               ----1---........--------........s-------........--------........ 
    0: mant 28, shift_left_num 59, offset 1, ahl = 0x0800000000000001  // pos  0000100000000000000000000000000000000000000000000000000000000001
    1: res = 0x7FFFFFFF                                                // >>28 0000000000000000000000000000000010000000000000000000000000000000
    2: res = 0x7FFFFFFF, sat_cnt = 1                                   //                                      1 means negative, sign changed!
    3: res = 0x7FFFFFFF, sat_cnt = 2

    0: mant 28, shift_left_num 59, offset 0, ahl = 0x0800000000000000
    1: res = 0x7FFFFFFF
    2: res = 0x7FFFFFFF, sat_cnt = 1
    3: res = 0x7FFFFFFF, sat_cnt = 2
    */
    const long long ahl = (1ULL << shift_left_num) + offset; 
    xy64_t          ahl_64;
    unsigned        sat_cnt = 0;
    int             res;

    printf ("\n0: mant %u, shift_left_num %u, offset %d, ahl = 0x%016llX\n", mant, shift_left_num, offset, ahl);

    res = sat31_int64 (ahl, mant);
    printf ("1: res = 0x%X\n", res);

    res = sat31_int64_B (ahl, mant, sat_cnt);
    printf ("2: res = 0x%X, sat_cnt = %u\n", res, sat_cnt);

    ahl_64.u.int64_val = ahl;

    res = sat31_int64_C (ahl_64, mant, sat_cnt);
    printf ("3: res = 0x%X, sat_cnt = %u\n", res, sat_cnt);
}
main

Code: Select all

int main(){
    printf("Hello, test with XC compiler (%s):\n", VER_STR);
    mymath_test   (mantissa);
    mymath_test_B (mantissa, mantissa+mantissa+2,  0); // 58
    mymath_test_B (mantissa, mantissa+mantissa+3, -1); // 59 
    mymath_test_B (mantissa, mantissa+mantissa+3,  1); // 59 Saturates
    mymath_test_B (mantissa, mantissa+mantissa+3,  0); // 59 Saturates

    printf("Bye\n");
    return 0;
}
By the way, where do the 0x7FFFFFFF sat value come from? I assume lsats produces it.

Observe that in one of my cases 0x7FFFFFFF is not a sat value.

Attached file:
mymath.s.txt
You do not have the required permissions to view the files attached to this post.
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
fabriceo
XCore Addict
Posts: 246
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi Øyvind
good approach to try and print everything.

but I feel important to reinforce the following because I don't feel comfortable with your second parameter (mantissa*2+x) which may bring confusions

the max positive in 32bits is 0x7FFFFFFF which represents 0.9999 in q31 notation
the min negative is 0x80000000 which represent exactly -1.0000 in q31 notation

if you are in q28, the exact same value represents +7.9999...-8.0

now, if you multiply two of these numbers say q28 by q28 you get q56 representing +127.99...-128.00
remark, the value will never be outside of +63.99...-64.00 as the 2 first bits will correspond to the sign of the result, in the new 64bit container.

if you apply an lsats of 28, this will saturate and force the value in the 64bits container to be within +7.99..-8.00 in q56 format.
Then lextract 28 will extract bits 60..28 so the value you get is still within +7.99..-8.00 but in the 32bit container represented as q28.

I recommend you do your tests with using the macro sxx() and fxx() because they abstract some of this complexity.

finally, during the night I came to the conclusion that to detect saturation, you just need to compare the MSB, but no need to compare LSB. a demonstration would be needed but let me propose these examples:

example (computed with an hexadecimal calculator)
+2.0 * +4.0 in q28 = +8.0 in q56, which is too much for a futur conversion in q28
0x2000 0000 * 0x4000 0000 = 0x0800 0000 0000 0000
after lsats 28 we will get by definition 7.999 in q56 so
0x07FF FFFF FFFF FFFF
I do not see a case where the lsb would be different and the msb identical

same with negative numbers:
(+2.0+1bit) * (-4.0) = -8.0-epsilon
0x2000 0001 * 0xC000 0000 = 0xF7FF FFFF C000 0000
after lsats 28 we ll get by definition -8.0 in q56 so 0xF800 0000 0000 0000
so here again msb is different so don't need to test lsb

food for thought
User avatar
aclassifier
Respected Member
Posts: 510
Joined: Wed Apr 25, 2012 8:52 pm

Post by aclassifier »

Thanks, again! (I hope that we have readers!)
Good tip to add the decimal point in the log. Now done. I didn't because I had a reasonably good mental understanding of it.
I think your discussion of the decimal point format stuff is kind of a summary of this. Agree.

Would have been fun to see whether xy64_t is "necessary", or whether the optimalization flattens that the same way as ah and al usage.

Plus. I love your conclusion that we don't need to compare the LSB. I had that stomach feeling, and had commented it in the previous code examples as well, but you broke through! I do agree with your conclusion, which spells out in this code:

Code: Select all

typedef struct {
    uint32_t lsb;
    uint32_t msb;
} parts64_t;

typedef struct {
    union {
        int64_t   int64_val;
        uint64_t  uint64_val;
        parts64_t parts64;
    } u;
} xy64_t;

static inline int32_t sat31_int64_C (const xy64_t ahl,  const int mant, unsigned &sat_cnt) {
    asm volatile("#sat31_int64_C:");
    xy64_t ahl_64 = ahl;
    const int pos=mant;
    asm("lsats %0,%1,%2":
        "=r"(ahl_64.u.parts64.msb),"=r"(ahl_64.u.parts64.lsb):
        "r"(pos),"0"(ahl_64.u.parts64.msb),"1"(ahl_64.u.parts64.lsb));
    // ===
    if (ahl_64.u.parts64.msb != ahl.u.parts64.msb) { // lsb testing not needed ref. fabriceo Fri May 31, 2024 8:59 am
        sat_cnt++;
    } else {}
    // ===
    asm("lextract %0,%1,%2,%3,32":
        "=r"(ahl_64.u.parts64.lsb):
        "r"(ahl_64.u.parts64.msb),"r"(ahl_64.u.parts64.lsb),"r"(pos));
    
    return ahl_64.u.parts64.lsb;
}
The logs. Here I have decorated with test_num as well as the decimal point values.

But again, it's the lsats that leaves the saturation value, right? Also again: Observe that in 2.0-2.3 res = 0x7FFFFFFF is not a sat value.

Code: Select all

                                                   7654321076543210
1.0: mant 28, shift_left_num 58, offset 0, ahl = 0x0400000000000000
1.1: res = 0x40000000 = 4.000000000000
1.2: res = 0x40000000 = 4.000000000000, sat_cnt = 0
1.3: res = 0x40000000 = 4.000000000000, sat_cnt = 0
                                                                             -----1--........--------........s-------........--------........ 
2.0: mant 28, shift_left_num 59, offset -1, ahl = 0x07FFFFFFFFFFFFFF // pos  0000011111111111111111111111111111111111111111111111111111111111
2.1: res = 0x7FFFFFFF = 7.999999996275                               // >>28 0000000000000000000000000000000001111111111111111111111111111111 
2.2: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 0
2.3: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 0
                                                                            ----1---........--------........s-------........--------........
3.0: mant 28, shift_left_num 59, offset 1, ahl = 0x0800000000000001 // pos  0000100000000000000000000000000000000000000000000000000000000001
3.1: res = 0x7FFFFFFF = 7.999999996275                              // >>28 0000000000000000000000000000000010000000000000000000000000000000
3.2: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 1
3.3: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 2

4.0: mant 28, shift_left_num 59, offset 0, ahl = 0x0800000000000000
4.1: res = 0x7FFFFFFF = 7.999999996275
4.2: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 1
4.3: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 2
And here are my tests, which print out the above:

Code: Select all

static inline void mymath_test_B (const unsigned test_num, const int mant, const unsigned shift_left_num, const int offset) {

    const long long ahl = (1ULL << shift_left_num) + offset; 
    xy64_t          ahl_64;
    unsigned        sat_cnt = 0;
    int             res;

    printf ("\n%u.0: mant %u, shift_left_num %u, offset %d, ahl = 0x%016llX\n", test_num, mant, shift_left_num, offset, ahl);

    res = sat31_int64 (ahl, mant);
    printf ("%u.1: res = 0x%X = %1.12f\n", test_num, res, fm(res,mant));

    res = sat31_int64_B (ahl, mant, sat_cnt);
    printf ("%u.2: res = 0x%X = %1.12f, sat_cnt = %u\n", test_num, res, fm(res,mant),sat_cnt);

    ahl_64.u.int64_val = ahl;

    res = sat31_int64_C (ahl_64, mant, sat_cnt);
    printf ("%u.3: res = 0x%X = %1.12f, sat_cnt = %u\n", test_num, res, fm(res,mant), sat_cnt);
}
I have kept the (mantissa*2+x) because I wanted to show on exacly which bit value the saturation started (for a positve number, mentally easier..):

Code: Select all

#define VER_STR \
  "v106"
// v106 31May2024 See fabriceo Fri May 31, 2024 8:59 am
// v105 30May2024 Teig as of Thu May 30, 2024 8:53 pm on XCore Exchange
// v104 30May2024 Teig ahl_sat_64
// v103 29May2024 Teig added sat_cnt, ok by Fabriceo Wed May 29, 2024 6:34 pm
// v102 18May2024 Fabrice new versions of both the .h and this file. See "Sat May 18, 2024 2:49 pm" there
// v101 13May2024 Fabriceo orig with -O3 and name _ends (cannnot see any difference in log)
// v100 13May2024 Fabriceo orig with -O2
int main(){
    printf("Hello, test with XC compiler (%s):\n", VER_STR);
    mymath_test   (mantissa);
    mymath_test_B (1, mantissa, mantissa+mantissa+2,  0); // 58
    mymath_test_B (2, mantissa, mantissa+mantissa+3, -1); // 59 
    mymath_test_B (3, mantissa, mantissa+mantissa+3,  1); // 59 Saturates
    mymath_test_B (4, mantissa, mantissa+mantissa+3,  0); // 59 Saturates

    printf("Bye\n");
    return 0;
}
...
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
aclassifier
Respected Member
Posts: 510
Joined: Wed Apr 25, 2012 8:52 pm

Post by aclassifier »

Since this started with a dsp_math_multiply_sat question, here is my new dsp_math_multiply_sat_cnt. Thanks for the help, fabriceo. I have tested this in my product proper, and it works.

dsp_math_multiply_sat_cnt:

Code: Select all

// "New" is new from dsp_math_multiply_sat in /lib_dsp/dsp_math.c
// Plus sat_cnt from https://www.xcore.com/viewtopic.php?p=41014#p41014 and the new code in /fabriceo_my_math/src/mymath.h
//
int32_t dsp_math_multiply_sat_cnt (
    const int32_t input1_value, 
    const int32_t input2_value, 
    const int32_t q_format, 
    unsigned      &sat_cnt) // New
{
    int32_t ah; uint32_t al;
    asm("maccs %0,%1,%2,%3"
        : "=r"(ah),"=r"(al)
        : "r"(input1_value),"r"(input2_value),"0"(0),"1"(1<<(q_format-1)));
    int32_t ah_mul = ah; // New
    asm("lsats %0,%1,%2"
        : "=r"(ah),"=r"(al)
        : "r"(q_format),"0"(ah),"1"(al));
    if (ah_mul != ah) { // New
        sat_cnt++;      // New
    } else {}           // New
    asm("lextract %0,%1,%2,%3,32"
        : "=r"(ah)
        : "r"(ah),"r"(al),"r"(q_format));
    return ah; // New comment: result from lextract is actually _al_, but since "al" here is unsigned, XMOS is reusing the signed "ah"
}
New total log:

Code: Select all

                                                   7654321076543210
1.0: mant 28, shift_left_num 58, offset 0, ahl = 0x0400000000000000
  1.1: res = 0x40000000 = 4.000000000000
  1.2: res = 0x40000000 = 4.000000000000, sat_cnt = 0
  1.3: res = 0x40000000 = 4.000000000000, sat_cnt = 0
1.4 dsp_math_multiply_sat_cnt with mant 28 but print decimal int32_t res with mant 0
  1.5: res = 0x00000002 = 2.000000000000, sat_cnt = 0 (268435456 * 2 >> 28 = 2)
                                                                             -----1--........--------........s-------........--------........ 
2.0: mant 28, shift_left_num 59, offset -1, ahl = 0x07FFFFFFFFFFFFFF // pos  0000011111111111111111111111111111111111111111111111111111111111
  2.1: res = 0x7FFFFFFF = 7.999999996275                             // >>28 0000000000000000000000000000000001111111111111111111111111111111 
  2.2: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 0
  2.3: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 0
2.4 dsp_math_multiply_sat_cnt with mant 28 but print decimal int32_t res with mant 0
  2.5: res = 0x00000004 = 4.000000000000, sat_cnt = 0 (536870912 * 2 >> 28 = 4)
                                                                             ----1---........--------........s-------........--------........ 
3.0: mant 28, shift_left_num 59, offset 1,  ahl = 0x0800000000000001 // pos  0000100000000000000000000000000000000000000000000000000000000001
  3.1: res = 0x7FFFFFFF = 7.999999996275                             // >>28 0000000000000000000000000000000010000000000000000000000000000000
  3.2: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 1
  3.3: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 2
3.4 dsp_math_multiply_sat_cnt with mant 28 but print decimal int32_t res with mant 0
  3.5: res = 0x00000006 = 6.000000000000, sat_cnt = 2 (805306368 * 2 >> 28 = 6)

4.0: mant 28, shift_left_num 59, offset 0, ahl = 0x0800000000000000
  4.1: res = 0x7FFFFFFF = 7.999999996275
  4.2: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 1
  4.3: res = 0x7FFFFFFF = 7.999999996275, sat_cnt = 2  
4.4 dsp_math_multiply_sat_cnt with mant 28 but print decimal int32_t res with mant 0
  4.5: res = 0x00000008 = 8.000000000000, sat_cnt = 2 (1073741824 * 2 >> 28 = 8)
Run as this:

Code: Select all

static inline void mymath_test_B (const unsigned test_num, const int mant, const unsigned shift_left_num, const int offset) {

    const long long ahl = (1ULL << shift_left_num) + offset; 
    xy64_t          ahl_64;
    unsigned        sat_cnt = 0;
    int             res;

    printf ("\n%u.0: mant %u, shift_left_num %u, offset %d, ahl = 0x%016llX\n", test_num, mant, shift_left_num, offset, ahl);

    res = sat31_int64 (ahl, mant);
    printf ("  %u.1: res = 0x%08X = %1.12f\n", test_num, res, fm(res,mant));

    res = sat31_int64_B (ahl, mant, sat_cnt);
    printf ("  %u.2: res = 0x%08X = %1.12f, sat_cnt = %u\n", test_num, res, fm(res,mant),sat_cnt);

    ahl_64.u.int64_val = ahl;

    res = sat31_int64_C (ahl_64, mant, sat_cnt);
    printf ("  %u.3: res = 0x%08X = %1.12f, sat_cnt = %u\n", test_num, res, fm(res,mant), sat_cnt);

    const int32_t mul1 = (int32_t)(test_num << mant);
    const int32_t mul2 = 2;
    res = dsp_math_multiply_sat_cnt (mul1, mul2, mant, sat_cnt);
    printf ("%u.4 dsp_math_multiply_sat_cnt with mant %u but print decimal int32_t res with mant 0\n", test_num, mant);
    printf ("  %u.5: res = 0x%08X = %1.12f, sat_cnt = %u (%d * %d >> %u = %d)\n", test_num, res, fm(res,0), sat_cnt, mul1, mul2, mant, res);
}
By the way, going for this did not change anything from both lsb and msb unsigned to unsigned only for lsb. It gives the same rsult, so I just assume it. Is this correct?

Code: Select all

// When used in "asm" it looks like the processor uses the type it's set up for, it's only the 32 bits with that counts,
// so differentiating between signed and unsiged is not strictly necessary
typedef struct {
    uint32_t u_lsb;
    int32_t  s_msb;
} parts64_t;

typedef struct {
    union {
        int64_t   int64_val;
        uint64_t  uint64_val;
        parts64_t parts64;
    } u;
} xy64_t;

static inline int32_t sat31_int64_C (const xy64_t ahl, const int mant, unsigned &sat_cnt) {
    asm volatile("#sat31_int64_C:");
    xy64_t ahl_64 = ahl;
    const int pos=mant;
    asm("lsats %0,%1,%2"
        : "=r"(ahl_64.u.parts64.s_msb),"=r"(ahl_64.u.parts64.u_lsb)
        : "r"(pos),"0"(ahl_64.u.parts64.s_msb),"1"(ahl_64.u.parts64.u_lsb));
    // ===
    if (ahl_64.u.parts64.s_msb != ahl.u.parts64.s_msb) { // lsb testing not needed ref. fabriceo Fri May 31, 2024 8:59 am
        sat_cnt++;
    } else {}
    // ===
    asm("lextract %0,%1,%2,%3,32"
        : "=r"(ahl_64.u.parts64.u_lsb)
        : "r"(ahl_64.u.parts64.s_msb),"r"(ahl_64.u.parts64.u_lsb),"r"(pos));
    
    return ahl_64.u.parts64.u_lsb;
}
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
fabriceo
XCore Addict
Posts: 246
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi Øyvind,
seems this topics comes to a great maturity in regards of your implementation of lsats. good.
it is true that sign or unsigned onset really make difference when you pass these to an asm statement.
But it is important to keep in mind that the MSB is signed and the LSB is unsigned.
This is important when you multiply, because as you now understood there is no instruction able to multiply unsigned (32) by signed (s+31). Also when doing shits rights in asm you cannot use shr but ashr.

best regards
fabriceo
User avatar
aclassifier
Respected Member
Posts: 510
Joined: Wed Apr 25, 2012 8:52 pm

Post by aclassifier »

In the xTIMEcomposer user guide (p399) we read

Code: Select all

Function    sats
Description Perform saturation on a 64-bit value.
            If any arithmetic has overflowed beyond a given bit index, 
            then the value is set to MININT or MAXINT, right shifted 
            by the bit index.
Type        signed long long lsats(signed long long value, unsigned int index)
Parameters  value The 64-bit value to perform saturation on.
            index The bit index at which overflow is checked for. 
Returns     The saturated 64-bit value.
The 64-bit value to perform saturation on in our examples are in "0"(ah),"1"(al). But does lsats really care about the signedness as seen in the xC code (and me as a programmer)? Aren't they just 32 bits registers and then treated by the lsats (microde) as it decides itself?

I tested this with the wrong signedness in one of my examples (both unsigned) and it seemed to work well. If I am right about this then the compiler just gives lsats the 32 bits values as registers. I am not able to further test it now.
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/