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.

TMS320F28335: Floating-point 'greater than' comparison failure ?

Part Number: TMS320F28335

I experienced a strange issue with the TMS320F28335.

When I perform a simple 'greater than' comparison with floating-point numbers, the comparison sometimes seem to give the wrong result.

In an effort to pin-point the problem I tested the following code:

if (test_variable > 100.0)
	{
		pjump++;
		asm("        MOV32 @_pregvalue, R0H");
		pjumpvalue = test_variable;
		test_variable = 100.0;
	}
	if (test_variable < 0.0)
	{
		njump++;
		asm("        MOV32 @_nregvalue, R0H");
		njumpvalue = test_variable;
		test_variable = 0.0;
	}

The asm() commands place the value of the R0H register, which is the register in which test_variable is moved to perform the floating-point comparison in the output .asm file, into the pregvalue or nregvalue variable (just in case)

I regularly monitored the value of these variables by outputting their values through a serial port. The data clearly shows that at some (seemingly random) times the code block after if (test_variable > 100.0) was executed even though the value of the test_variable was lower than 100.0. This occurred at a frequency of, say, once every few hours. However, the same was never observed for the second (<) comparison. 

Is this a known issue? It seems possible to get around it using the __fmin() and __fmax() functions instead of if() tests, but I still find it slightly disturbing. 

Thanks in advance,

Laurent Badel

For info, the relevant part of the generated assembly is as follows - I couldn't see anything wrong with it:

MOV32 R0H,@_test_variable
CMPF32 R0H,#17096
MOVST0 ZF, NF
BF $C$L16,GT

...

$C$L16:
INC @_pjump
MOV32 @_pregvalue, R0H
MOVL ACC,@_test_variable
MOVL @_pjumpvalue,ACC
MOVIZ R0H,#17096
MOV32 @_test_variable,R0H

  • Laurent,

    Certainly the code should not be doing that, and like you, I don't see a problem in the assembly code you pasted. I've experimented with similar code and nothing I've tried so far behaves unexpectedly.

    Could you say something about the surrounding code and the way "test_variable" is being manipulated please? Also, is the code snippet in a loop, and if so could you send the loop code?

    I'd also like to check physically where the code is in memory, so could you paste the dis-assembly window too? Thanks.

    Regards,

    Richard
  • Richard,

    Thank you very much for your reply. 

    The surrounding code is rather complex as this is running inside a RTOS (based on MuC/OS I believe, but not exactly the identical). I will think about how to describe the surrounding concisely while remaining as accurate as possible. I will also look at how to produce the disassembly.

    In the meantime please have a look at these two rows of the data that I convinced me of the issue - perhaps you might have an alternative explanation for this.

    time test_variable pjump pjumpval pregval njump njumpval nregval
    2018-02-08 15:15:00:433860 55.8 129 55.8 55.8 12065 0 0
    2018-02-08 15:15:00:996350 55.8 130 55.8 55.8 12065 0 0

    As noted earlier, I gathered this data by outputting the value of the monitored variable through a serial port (this is a kind of debugging console that is running as a task in the RTOS). In general I am able to see the values with an update rate of once every 600ms or so. Perhaps worth noting also is that the task in which the incriminated code is running is a low-priority one (second lowest out of 10 tasks). 

    In the above data you can see that within a ~600ms interval, the value of pjump changed from 129 to 130. The value of 129 can be disregarded as this came from earlier cases where test_variable was indeed > 100.0 and so the code was legitimately executed. However in this particular case you can see that test_variable = 55.8 so that there is no reason that the block of code after the > comparison be executed. 

    The reason why test_variable does not get assigned the value 100.0 is that I was simultaneously testing a mechanism to prevent the jump (simply by enforcing that if the value changes by more than 0.1 in one iteration we revert to the previous value), but I can confirm that it indeed had the value 100.0 as I was storing this value in another variable be reverting to the previous value.  

    We can also see that pjumpval and pregval have the same value as test_variable so that we can likely exclude a scenario in which (i) test_variable was indeed > 100.0 when the comparison was performed but its value somehow changed after and (ii) a context change occurred between the moment when test_variable's value was pushed to the R0H register and the moment where the CMPF32 operation was executed, and the value of R0H was changed out of this context.

    Given this data, the only explanation I can think of is that the floating-point comparison gave the wrong result, but perhaps I am missing something?

    Thanks, 

    Laurent

  • Unfortunately I don't have the exact code, in which I observed this issue, anymore. I have recompiled a version with the portion of test code in my original post included, and am running a test to see if the issue occurs again. If it does, I will post the disassembly but as far as I could see the disassembled .obj file did not differ from the original .asm file.

    Thanks,

    Laurent
  • Laurent,

    Thank you for the additional information. I do not think the reason for the issue lies in the assembly code you posted. If it did, we would be able to reproduce it in a simple test case. My feeling is that the issue is most likely related to the RTOS task or interrupts. The fact that it takes such a long time to happen points to a specific set of conditions which occur only very rarely.

    I was interested in the surrounding code to see whether any branch instructions are in proximity to the suspect lines. If so, we have to consider the pipeline effects of a branch, particularly in combination with a context switch. I am also interested in physically where in memory the instructions are located - which memory block are they in and at what addresses?

    Can you say a little more about the RTOS please?

    Regards,

    Richard
  • Richard,

    Thank you very much for your reply.

    I haven't been able to replicate the issue so far, but will keep trying.

    > I was interested in the surrounding code to see whether any branch instructions are in proximity to the suspect lines

    This code executed on a timer with a frequency of once per 100 ms.

    The whole process is as follows

    // This code executed every 100ms
    
    if (x < T && (-1 * x) < T)
    {
    	// Removed most of this part as it is not relevant
    	if(!flag) flag = true;
    }
    else
    {
    	if(flag) flag = false;
    	update(x); // suspect code inside this function
    }
    

    with the update() function:

    void  update(long x)
    {
    	float increment = dt * slope * ((float) x) / 100.0;
    	if ((increment > 1.0) || ((-increment) > 1.0))
    	{
    		return; // this never happens in practice
    	}
    
    	if (x > 0)
    	{
    		estimate -= increment;
    	}
    	else
    	{
    		estimate -= increment * eff;
    	}
    
    	// DEBUG - To find out why estimate sometimes jumps to 100
    if (increment > incrementmax) incrementmax = increment; if (-increment > incrementmax) incrementmax = -increment; if (estimate > estimatemax) estimatemax = estimate; if (-estimate > estimatemax) estimatemax = estimate; if (estimate > 100.0) { pjump++; asm(" MOV32 @_pregvalue, R0H"); pjumpvalue = estimate; estimate = 100.0; } if (estimate < 0.0) { njump++; asm(" MOV32 @_nregvalue, R0H"); njumpvalue = estimate; estimate = 0.0; } }

    > I am also interested in physically where in memory the instructions are located - which memory block are they in and at what addresses

    I am not sure how to find out where the instructions are located in memory. Could you explain how?

    > Can you say a little more about the RTOS please?

    It is a simple RTOS that, I believe, is quite similar to µC/OS II but only the core features seem to be included.

    As far as I understand, it can be simply described as follows.

    - The OS maintains a TCB and a stack for each task.

    - An ISR associated with TINT2 seems to (1) enable interrupts (EINT) to, (2) let any pending ISRs execute, (3) check if a task with a priority higher than the current task is ready, if so (4) perform a context switch toward the high priority task, and (5) disable interrupts (DINT).

    - The context switch function is written in assembly and essentially pushes all register values and the value of the CPU stack pointer on the current stack, changes the CPU's stack pointer to the location of the high-priority task and pulls the previously saved register values).

    - There is a second possibility for context switches to occur outside interrupts, through a OS scheduling function that is called by each task to wait for OS events (mainly timer events). This context switch is done by issuing the TRAP #31 instruction to execute some assembly code that essentially does the same as described above.

    I hope this helps,

    Laurent

     

     

  • Richard,

    I was able to reproduce the problem. As you can see from the attached graph, a sudden jump in the 'estimate' variable occurred two times (red dots) with an interval of about 9 hours in-between. There are other 'jumps' but these are legitimate - due to parts of the algorithm that I did not describe in my previous post (as these are, I believe, irrelevant to the problem at hand).

    Since the code is executed every 100ms, this is around 300'000 floating point comparisons between the first and the second jump. As I mentioned in an earlier post, I have never seen the same problem occur with the "<" operator.

    Here is the data captured around the first jump:

    time estimate x flag slope*1e6 pjump pjumpvalue     pregvalue    
    2018-02-28 20:29:28:760290 30.2 14.64 0 654.9 0 0 0
    2018-02-28 20:29:29:316060 30.2 14.64 0 654.9 0 0 0
    2018-02-28 20:29:30:159620 30.2 14.64 0 654.9 0 0 0
    2018-02-28 20:29:30:724510 30.2 14.64 0 654.9 0 0 0
    2018-02-28 20:29:31:284200 30.2 14.66 0 654.9 1 30.2 30.2
    2018-02-28 20:29:32:123300 99.9 14.66 0 654.9 1 30.2 30.2
    2018-02-28 20:29:32:687590 99.9 14.66 0 654.9 1 30.2 30.2
    2018-02-28 20:29:33:248210 99.9 14.64 0 654.9 1 30.2 30.2
    2018-02-28 20:29:33:811680 99.9 14.64 0 654.9 1 30.2 30.2

    And around the second jump:

    time estimate x flag slope*1e6 pjump pjumpval pregval
    2018-03-01 05:16:28:974400 48 20.94 0 612.4 119 100 100
    2018-03-01 05:16:29:814600 48 20.94 0 612.4 119 100 100
    2018-03-01 05:16:30:379390 48 20.94 0 612.4 119 100 100
    2018-03-01 05:16:30:939200 48 20.94 0 612.4 119 100 100
    2018-03-01 05:16:31:782660 48 20.94 0 612.4 119 100 100
    2018-03-01 05:16:32:347270 48 20.94 0 612.4 120 48 48
    2018-03-01 05:16:32:903460 99.9 20.94 0 612.4 120 48 48
    2018-03-01 05:16:33:470870 99.9 20.94 0 612.4 120 48 48
    2018-03-01 05:16:34:310930 99.9 20.94 0 612.4 120 48 48

    I will post the disassembly of the .obj file shortly. Please let me know if there is anything you would like to see.

    Thanks,

    Laurent

  • Also worth noting is that the other comparisons

        if (increment > incrementmax) incrementmax = increment;
        if (-increment > incrementmax) incrementmax = -increment;
        if (estimate > estimatemax) estimatemax = estimate;
        if (-estimate > estimatemax) estimatemax = estimate;
    
    

    Also seem to give the wrong result a number of times. The variable incrementmax is intended to hold the maximum absolute value of the variable increment (i.e., two comparisons are made to give the effective result "incrementmax = max(incrementmax, abs(increment);" ), and the same goes for the other variable estimatemax.

    Given the above code, both these variable should never, in principle, decrease in value. But here is the recorded data:

    As you can see, there are many instances in which the value decreases, whereas as far as I can see, this should not be possible.
    I've search the code and I am 100% sure that these variables are accessed nowhere else in the code, except in the debugger in which they are read, but of course not written.

  • Here is the disassembly of the update() function (obtained by running dis2000.exe on the .obj file)

    000051:              _update:
    00000051   b2bd       MOVL         *SP++, XAR1
    00000052   fe02       ADDB         SP, #2
    00000053   8ba9       MOVL         XAR1, ACC
    00000054   761f       MOVW         DP, #0x0
    00000055   0000
    00000056   e2af       MOV32        R0H, @0x34, UNCF
    00000057   0034
    00000058   bda1       MOV32        *(0:0x0f16), XAR1
    00000059   0f16
    0000005a   7700       NOP          
    0000005b   7700       NOP          
    0000005c   7700       NOP          
    0000005d   7700       NOP          
    0000005e   e689       I32TOF32     R2H, R1H
    0000005f   000a
    00000060   e2af       MOV32        R1H, @0x2a, UNCF
    00000061   012a
    00000062   e700       MPYF32       R0H, R1H, R0H
    00000063   0008
    00000064   e802       MOVIZ        R1, #0x42c8
    00000065   1641
    00000066   e700       MPYF32       R0H, R2H, R0H
    00000067   0010
    00000068   7640       LCR          0x000000
    00000069   0000
    0000006a   e811       CMPF32       R0H, #0x3f80
    0000006b   fc00
    0000006c   ad14       MOVST0       NF,ZF
    0000006d   56c2       BF           112, GT
    0000006e   0070
    0000006f   e6cf       MOV32        R1H, R0H, UNCF
    00000070   0001
    00000071   e6af       NEGF32       R1H, R1H, UNCF
    00000072   0009
    00000073   e811       CMPF32       R1H, #0x3f80
    00000074   fc01
    00000075   ad14       MOVST0       NF,ZF
    00000076   56c2       BF           103, GT
    00000077   0067
    00000078   b2a9       MOVL         ACC, XAR1
    00000079   56c2       BF           17, GT
    0000007a   0011
    0000007b   761f       MOVW         DP, #0x0
    0000007c   0000
    0000007d   e2af       MOV32        R1H, @0x26, UNCF
    0000007e   0126
    0000007f   e700       MPYF32       R1H, R0H, R1H
    00000080   0041
    00000081   e2af       MOV32        R2H, @0x2e, UNCF
    00000082   022e
    00000083   e720       SUBF32       R1H, R2H, R1H
    00000084   0051
    00000085   7700       NOP          
    00000086   e203       MOV32        @0x2e, R1H
    00000087   012e
    00000088   56cf       BF           11, UNC
    00000089   000b
    00008a:              $C$L3:
    0000008a   761f       MOVW         DP, #0x0
    0000008b   0000
    0000008c   e2af       MOV32        R1H, @0x2e, UNCF
    0000008d   012e
    0000008e   e720       SUBF32       R1H, R1H, R0H
    0000008f   0009
    00000090   7700       NOP          
    00000091   e203       MOV32        @0x2e, R1H
    00000092   012e
    000093:              $C$L4:
    00000093   e2af       MOV32        R1H, @0x1a, UNCF
    00000094   011a
    00000095   e694       CMPF32       R0H, R1H
    00000096   0008
    00000097   ad14       MOVST0       NF,ZF
    00000098   56c5       BF           4, LEQ
    00000099   0004
    0000009a   e203       MOV32        @0x1a, R0H
    0000009b   001a
    00009c:              $C$L5:
    0000009c   e6af       NEGF32       R0H, R0H, UNCF
    0000009d   0000
    0000009e   e2af       MOV32        R1H, @0x1a, UNCF
    0000009f   011a
    000000a0   e694       CMPF32       R0H, R1H
    000000a1   0008
    000000a2   ad14       MOVST0       NF,ZF
    000000a3   56c5       BF           4, LEQ
    000000a4   0004
    000000a5   e203       MOV32        @0x1a, R0H
    000000a6   001a
    0000a7:              $C$L6:
    000000a7   e2af       MOV32        R0H, @0x28, UNCF
    000000a8   0028
    000000a9   e2af       MOV32        R1H, @0x2e, UNCF
    000000aa   012e
    000000ab   e694       CMPF32       R1H, R0H
    000000ac   0001
    000000ad   ad14       MOVST0       NF,ZF
    000000ae   56c5       BF           4, LEQ
    000000af   0004
    000000b0   062e       MOVL         ACC, @0x2e
    000000b1   1e28       MOVL         @0x28, ACC
    0000b2:              $C$L7:
    000000b2   e2af       MOV32        R0H, @0x2e, UNCF
    000000b3   002e
    000000b4   e6af       NEGF32       R0H, R0H, UNCF
    000000b5   0000
    000000b6   e2af       MOV32        R1H, @0x28, UNCF
    000000b7   0128
    000000b8   e694       CMPF32       R0H, R1H
    000000b9   0008
    000000ba   ad14       MOVST0       NF,ZF
    000000bb   56c5       BF           4, LEQ
    000000bc   0004
    000000bd   e203       MOV32        @0x28, R0H
    000000be   0028
    0000bf:              $C$L8:
    000000bf   e2af       MOV32        R0H, @0x2e, UNCF
    000000c0   002e
    000000c1   e812       CMPF32       R0H, #0x42c8
    000000c2   1640
    000000c3   ad14       MOVST0       NF,ZF
    000000c4   56c2       BF           16, GT
    000000c5   0010
    000000c6   e5a0       CMPF32       R0H, #0.0
    000000c7   ad14       MOVST0       NF,ZF
    000000c8   56c3       BF           21, GEQ
    000000c9   0015
    000000ca   0a07       INC          @0x7
    000000cb   e203       MOV32        @0x18, R0H
    000000cc   0018
    000000cd   062e       MOVL         ACC, @0x2e
    000000ce   1e16       MOVL         @0x16, ACC
    000000cf   e590       ZERO         R0
    000000d0   e203       MOV32        @0x2e, R0H
    000000d1   002e
    000000d2   56cf       BF           11, UNC
    000000d3   000b
    0000d4:              $C$L9:
    000000d4   0a09       INC          @0x9
    000000d5   e203       MOV32        @0x1c, R0H
    000000d6   001c
    000000d7   062e       MOVL         ACC, @0x2e
    000000d8   1e14       MOVL         @0x14, ACC
    000000d9   e802       MOVIZ        R0, #0x42c8
    000000da   1640
    000000db   e203       MOV32        @0x2e, R0H
    000000dc   002e
    0000dd:              $C$L10:
    000000dd   fe82       SUBB         SP, #2
    000000de   8bbe       MOVL         XAR1, *--SP
    000000df   ff69       SPM          #0
    000000e0   0006       LRETR      
    
    

    The suspect branches are located, I believe, at
    00000098   56c5       BF           4, LEQ

    000000a3   56c5       BF           4, LEQ

    000000ae   56c5       BF           4, LEQ

    000000bb   56c5       BF           4, LEQ

    000000c4   56c2       BF           16, GT

    The instructions seems to be located in the ".text" section (the disassembled .obj file starts with .sect ".text"), which is in memory page 0, starts at address 003020f4 and has length 00011bcb.
    The .obj file itself is starts at memory address 00311de2 and has length 00000280.

    Please let me know if there is anything else I can provide.

    Thank you,

    Laurent

  • Laurent,

    Thank you for providing the additional information.  On some devices there was an issue with branch instructions near the end of valid memory.  I am travelling at the moment so don't have all the information in front of me, but from the addresses above this doesn't appear to be the issue. Thank you for the disassembly screen capture. 

    The fact that the anomalies appear rarely tallies with my own experience of an OS bug several years ago in which a critical register was not being protected by the context save. As I said in an earlier post, I believe this is the most likely cause of the problem.  I hesitate to ask this, but do you think it's worth trying TI RTOS to see if you can reproduce the problem with that?

    One other thing I'd like to check is the stack.  Do you have any monitoring on the stack to be sure there is no over-flow?  For example, could you fill stack memory with known data and check later for a "high water mark"?  There is an application note on stack over-flow protection (spra820, I think) which might also help.

    I am travelling until next week but will monitor this forum whenever I can. Thanks for your patience.

    Regards,

    Richard

     

  • Richard,

    Thank you very much for the quick feedback. I'm not sure if I will be able to try out TI RTOS, as this might involve quite a lot of work to migrate to a new OS. I will have a look at the documentation nonetheless to see if it would be feasible.

    - Concerning the possibility of an OS bug, I'm wondering if there is a way to test this more precisely. I saved the value of the R0H register to check whether the instruction "CMPF32       R0H, #0x42c8" was being executed with a corrupted value in the R0H register - from the data this doesn't seem to be the case.

    Another possibility would be that the ST0 register, which is used by the BF instruction, is being corrupted after the ZF and NF flags have been moved to it. This would require that a context change occur exactly between, e.g., the c7 and c8 instructions:

    000000c7   ad14       MOVST0       NF,ZF

    000000c8   56c3       BF           21, GEQ

    I'm not sure if this is possible, but would there be a way to save the value of the ST0, and perhaps also the STF registers, into variables?

    Here is the code for the assembly macro that pushes the registers on the stack in the OS:

    REGSAVE         .macro
                    push        DP:ST1                  ; Save SPA Flag
                    push        AR1H:AR0H
                    push        XAR2
                    push        XAR3
                    push        XAR4
                    push        XAR5
                    push        XAR6
                    push        XAR7
                    push        XT
                    push        RPC
    
                    .if         .TMS320C2800_FPU32
                    push        RB
                    mov32       *SP++,R0H
                    mov32       *SP++,R1H
                    mov32       *SP++,R2H
                    mov32       *SP++,R3H
                    mov32       *SP++,R4H
                    mov32       *SP++,R5H
                    mov32       *SP++,R6H
                    mov32       *SP++,R7H
                    mov32       *SP++,STF
                    .endif

    It seems that the ST0 register is not being saved. Does this mean that a context switch occurring immediately after the instruction at c7 could potentially modify the contents of ST0 before the code resumes at instruction c8? If so, would adding PUSH T:ST0 / POP T:ST0 as appropriate be sufficient to solve the issue?

    - Regarding the stack, I had already quite extensively monitored stack usage in connection with a different problem. As I did not find any problems back then, but decided to double the stack size anyway for safety, I seriously doubt the possibility of a stack overflow.

    Best regards,

    Laurent

  • I found out from the DSP documentation that the ST0 register is automatically saved when an interrupt is executed:

    3. Prepare for the interrupt service routine and save register values. The main tasks performed in
    this phase are:
    • Complete execution of the current instruction and flush from the pipeline any instructions that have
    not reached the decode 2 phase.
    • Automatically save most of the current program context by saving the following registers to the
    stack: ST0, T, AL, AH, PL, PH, AR0, AR1, DP, ST1, DBGSTAT, PC, and IER.
    • Fetch the interrupt vector and load it into the program counter (PC). For devices with a PIE
    module, the vector fetched will depend on the setting of the PIE enable and flag registers.

    Thus it seems correct not to save ST0 in the above "REGSAVE" routine.

  • I am now able to run the program in debug mode in CCS.

    I have tested a version in which I did not start the RTOS but simply ran the faulty code in a loop inside the main() function. I did not encounter any problems in this case, so the problem probably lies with the RTOS, as you suggested.

    I am now running a version with the RTOS but only two out of 10 tasks running - I should be able to see if the issue appears also in this case by next Monday. I have set a breakpoint to stop execution immediately after "if(estimate > 100.0)", so if the program stops there I should be able to see the contents of the registers and memory at that particular point.

    If you have any suggestions regarding what to look for specifically, I would be very happy to hear them.

    Thanks,

    Laurent

  • Laurent,
    Thank you for doing this and making the additional tests. I think it will be worthwhile.
    I will have to pass this thread to a colleague but it will be followed up so please post back with any additional findngs. Many thanks.
    Regards,
    Richard
  • Richard,

    Thank you very much for your kind support. 

    So far I haven't been able to reproduce the issue in debug mode. The program is part of a system with mutliple PCBs which makes it difficult to reproduce the exact same conditions. I will keep trying and update the post with any relevant finding. 

    Best regards,

    Laurent

  • Laurent,

    You are welcome, I wish I could do more. 

    Thank you for running the tests, and I fully appreciate the difficulty in reproducing the issue.  My feeling is that you will probably not see the issue with TI-RTOS, in which case I would strongly suspect an incomplete context save is the root cause.

    As I mentioned, I have to hand off this thread to a colleague as I have a personal issue to attend to, but please do post back with any additional information you have and someone here will help you.

    Regards,

    Richard

  • Hello, 

    I was able to reproduce the problem in the debugger. 

    The code branched from 311d7e to 311d8e.

    At instruction 311d7b, the code compares the value of the R0H register to the immediate value 0x42c8 = 100.0. It should branch to C$L9 only if R0H > 100.0.

    However, the value of the R0H register is 22.445724:

    The documentation says that the "GT" instruction will branch if the condition "Z=0 AND N=0" is true. The value of the Z and F flags in the ST0 register is copied from STF's ZF and NF at 311d7d "MOVST0 NF,ZF". 
    As we can see from the above picture, in the STF register both flags are indeed 0, and in the ST0 register

    both flags are also 0. 
    Thus the code branching C$L9 is the corrrect thing to do.

    As far as I can see, this leaves the following possibilities:

    1. Instruction  CMPF32       R0H, #0x42c8 failed, i.e., did not assign the correct value to ZF and NF

    2. The value of ZF and/or NF changed between the two instructions

    311d7b: E8121640 CMPF32 R0H, #0x42c8
    311d7d: AD14 MOVST0 NF,ZF

    resulting in the Z,F flags being assigned ZF and NF's corrupted values

    3. The value of both  ST0 and STF was corrupted between the two instructions

    311d7d: AD14 MOVST0 NF,ZF
    311d7e: 56C20010 BF C$L9, GT

    (highly unprobable)

    The most probable one being likely #2.


    In order to realize #2 it seems necessary that an OS context change occur between instructions 311d7b and 311d7d.

    => Is this possible? I don't understand the details of the pipeline. Our RTOS can trigger context changes from an ISR associated with TINT2. 

    => Any alternative explanations? 

    => Is there anything else worth investigating while the program is halted at that point? I will keep it halted for some time in case someone has any suggestions. 

    Thanks in advance,

    Laurent

  • I've checked all the interrupts and all of them save and restore STF and at least R0H - given this I don't see how any interrupt could modify the STF flag. 
    I will try to run the code again with interrupts disabled around the faulty part to see if the problem is indeed due to interrupts.

    By the way, saving of the FPU registers seems to be automatically generated by the compiler (it is not done explicitly in the interrupts' c code) but I'm not sure for what reason.

    For example, one very simple interrupt gets compiled as (this is the only interrupt that does not save R0x registers, but as it does not modify them I don't see a problem with that):

            ASP       
            PUSH      RB                    
            MOV32     *SP++,STF             
            SETFLG    RNDF32=1, RNDF64=1    
            CLRC      PAGE0,OVM             
            CLRC      AMODE                 
            MOVW      DP,#_PieCtrlRegs+1    
            MOVB      @_PieCtrlRegs+1,#1,UNC 
            MOV32     STF,*--SP             
            POP       RB                    
            NASP      
            IRET      

    I'm not sure why the code pushes STF on the stack and later restores it even though it does not use it. Is there an explanation for this? 

    Thanks,

    Laurent

  • I've made some progress - it now seems clear that the STF and ST0 registers are being changed by one of the interrupts. 

    I modified the code to copy the ZF & NF flags to a variable immediately after the CMPF32 operation, with interrupts disabled.
    I then re-enable interrupts and wait for some time to allow interrupts to kick in, and then branch based on the current value of ZF & NF. 

    The code is as follows:

    ; Disable interrupts 
            setc	INTM
    
    ; compare variable estimate to 100.0 and store the results in ZF/NF flags
            MOV32     R0H,@_estimate    
            CMPF32    R0H,#17096         
    
    ; Copy ZF/NF flags to variable STFprev
            SPM       #0                   
            LCR       #_getSTF               
            MOVW      DP,#_STFprev
            MOV       @_STFprev,AL
    
    ; Re-enable interrupts
            clrc	INTM
    
    ; Copy ZF/NF flags to ST0 register and wait some cycles to increase the likelihood of an interrupt
            MOVST0    ZF, NF 
            NOP
    ; Many other NOP's
            NOP
    
    ;Branch if Z=0 and N=0
            BF        $branch$1,GT
    
    ; Some code
    BF        $branch$2,UNC
    
    $branch$1:                              
            ESTOP0                                 ; breakpoint                         
            INC       @_pjump               
            MOV32 @_pregvalue, R0H          
            MOVL      ACC,@_estimate    
            MOVL      @_pjumpvalue,ACC      
            MOVIZ     R0H,#17096            
            MOV32     @_estimate,R0H  
    
    $branch$2:  
    ; Some code and return
    
    

    Adding a series of NOP instructions after the MOVST0 ZF, NF instruction had the desired effect of increasing the likelihood of the program branching to $branch$1: it now consistently halts after only a few seconds versus a few hours previously without the NOPs. 

    On halting, the variable STFprev is seen to be:

    STFprev unsigned int 0000000010100100 (Binary) 0x0000BFCA@Data

    meaning that Z=0 and N=1, as is expected if (estimate > 100.0) is false. However the Z/ZF and N/NF flags in the ST0/STF registers are all 0. 

    I also added counters to find out which ISRs were executed and find that 4 ISRs were executed a cumulative number of 10 times between the clrc INTM instruction and the ESTOP0 instruction. 

    With this I think I should be able to progressively zero in on the issue.

    Edit: the code for the function getSTF() is

        .sect	".text"
        .asmfunc          
    _getSTF:              
        MOVST0    ZF, NF  
        PUSH    ST0       
        POP     AL        
        LRETR             
        .endasmfunc 

  • I think I found the cause of the issue: when a context switch occurs, the RTOS restores the FPU registers in the following way

                    mov32       STF,*--SP
                    mov32       R7H,*--SP
                    mov32       R6H,*--SP
                    mov32       R5H,*--SP
                    mov32       R4H,*--SP
                    mov32       R3H,*--SP
                    mov32       R2H,*--SP
                    mov32       R1H,*--SP
                    mov32       R0H,*--SP

    This gets translated as:

    3135a0:   E28000BE    MOV32        STF, *--SP
    3135a2:   E2AF07BE    MOV32        R7H, *--SP, UNCF
    3135a4:   E2AF06BE    MOV32        R6H, *--SP, UNCF
    3135a6:   E2AF05BE    MOV32        R5H, *--SP, UNCF
    3135a8:   E2AF04BE    MOV32        R4H, *--SP, UNCF
    3135aa:   E2AF03BE    MOV32        R3H, *--SP, UNCF
    3135ac:   E2AF02BE    MOV32        R2H, *--SP, UNCF
    3135ae:   E2AF01BE    MOV32        R1H, *--SP, UNCF
    3135b0:   E2AF00BE    MOV32        R0H, *--SP, UNCF

    Now, according to the documentation, the  instruction MOV32 RaH, mem32, UNCF seems to modify some flags of the STF registers, including ZF and NF.

    Specifically, the documentation specifies for the UNCF condition:

    (2) This is the default operation if no CNDF field is specified. This condition will allow the ZF, NF, ZI, and NI flags to be modified
    when a conditional operation is executed. All other conditions will not modify these flags.

    The meaning of this is not entirely clear to me, but I have observed the STF register being modified when stepping through one of the MOV32 operations. 

    This explains why, if a context switch occurs directly after a CMPF32 instruction, for example, when the context is later restored the state of the STF register may be changed. If a branching instruction follows, it may thus yield an unexpected result. 

    I think a very simple solution would be to store the STF register first, and thus restore it last, or else to specify the "UNC" condition in the .asm source file. Would this work out? Which should be preferred?

    Thanks, 

    Laurent

  • Hi Laurent,

    Thanks for the update. The conclusions that you've reached makes sense to me. I checked the TI-RTOS context save/restore code out of curiosity and can confirm that they save STF first and restore it last.

    Whitney
  • Hi Whitney,

    Thanks for your reply.

    I checked step-by-step in the debugger and this is exactly what happens: the "MOV32        R7H, *--SP, UNCF" instruction overwrites the restored STF register. Simply explicitly adding the "UNC" condition to all RaH moves in the source .asm code preserves the STF register and solves the issue.

    Richard, your initial feeling was correct!

    Thanks again to both of you for the support.

    I think this closes the issue.

    Laurent