Shop OBEX P1 Docs P2 Docs Learn Events
Mothra - Major Propeller Problem or At Least To Me It Is — Parallax Forums

Mothra - Major Propeller Problem or At Least To Me It Is

idbruceidbruce Posts: 6,197
edited 2017-06-29 05:02 in Propeller 1
Hello Everyone

I have some code that has been giving me some serious grief, and tracking down the source of my grief has been a real pain.

A large portion of this grief has been caused by mathematical operations, performed either immediately before or during the processing of a waitcnt(). These grief stricken operations apply to both adding and subtracting, but for the sake of simplicity, I will just discussion the subtraction portion of my grief. To make matters worse, I had no problems with performing these operations in a similar fashion in different code.

Anyhow please notice my grief :(

Program hangs at runtime
follower_1_start_stop_speed--;
follower_1_start_stop_speed--;
follower_1_start_stop_speed--;

waitcnt(follower_1_start_stop_speed + CNT);


Program hangs at runtime
follower_1_start_stop_speed -= 3;

waitcnt(follower_1_start_stop_speed + CNT);


Program hangs at runtime
follower_1_start_stop_speed = follower_1_start_stop_speed - 3;

waitcnt(follower_1_start_stop_speed + CNT);


Program hangs at runtime
waitcnt((follower_1_start_stop_speed -= follower_1_ramp_inc_dec) + CNT);


Program hangs at runtime
waitcnt((follower_1_start_stop_speed -= 3) + CNT);


Program runs perfectly, but without the necessary subtraction
waitcnt(follower_1_start_stop_speed + CNT);

I would say "good grief", but this is some very bad grief!
«1

Comments

  • The first thing that comes to mind is that follower_1_start_stop_speed value is too low and waitcnt waits for the counter to roll over after about 53 seconds.
    I can't believe that a simple subtraction causes this kind of problem, however it is possible if the value of follower_1_start_stop_speed is exactly the time needed for the waitcnt to execute. Looks like you are using C, so remember that machine code instructions takes at least 16 clock cycles to execute in LMM (more if you are using CMM) and even the simplest function may take a number of instructions.
    It would help if you could post a complete, reproducible code snippet.
  • macca

    As seen in the code below, follower_1_start_stop_speed starts out with a value of 14000. In it's current state, this code will run and output to the serial terminal, but if you try to change it to using one of the subtractions that have been commented out, it will hang during the first loop.
    #include "simpletools.h"
    
    int main()
    {
    	uint8_t follower_1_dir = 0;
    	uint8_t follower_1_dir_pin = 1;
    	uint8_t follower_1_step_pin = 2;
    	uint32_t follower_1_ramp_up_steps = 10;
    	uint32_t follower_1_running_steps = 30;
    	uint32_t follower_1_ramp_down_steps = 10;
    	uint32_t follower_1_pulse_width = 385;
    	uint32_t follower_1_offset;
    	uint32_t follower_1_start_stop_speed = 14000;
    	uint32_t follower_1_ramp_inc_dec = 3;
    	double follower_1_int_part = 2;
    	double follower_1_dec_part = 0.631578947368421;
    
    	uint32_t copy_1_masters_pulse_width = 385;
    
    	////////////////////////////////////////////////////////////
    	// This variable is only for testing purposes and it can be
    	// deleted, after testing is completed.
    	uint32_t follower_1_steps_made;
    	////////////////////////////////////////////////////////////
    
    	int32_t follower_1_counter;
    	int32_t follower_1_step_counter;
    
    	////////////////////////////////////////////////////////////
    	// This variable is only for testing purposes and it can be
    	// deleted, after testing is completed.
    	follower_1_steps_made = 0;
    	////////////////////////////////////////////////////////////
    
    	// Set DIRA as an output.
    	DIRA |= 1 << follower_1_dir_pin;
    
    	// Set DIRA as an output.
    	DIRA |= 1 << follower_1_step_pin;
    
    	// Set the OUTA register to match the desired direction of rotation.
    	if(follower_1_dir == 0)
    	{
    		OUTA &= (~1 << follower_1_dir_pin);
    	}
    	else
    	{
    		OUTA |= 1 << follower_1_dir_pin;
    	}
    
    	// Get the current System Counter value.
    	follower_1_counter = CNT;
    
    	// Maintain a ramp up consistent with the master's ramp up.
    	while(follower_1_ramp_up_steps != 0)
    	{
    		// Pause for a high pulse on the master's step pin.
    		waitcnt(copy_1_masters_pulse_width + CNT);
    
    		// Maintain a step count consistent with the master's steps.
    		follower_1_step_counter++;
    
    		// Determine if the follower should make a step, if so, then make it.
    		// However keep in mind that all timing is critical at this point.
    		// So adjust timers appropriately
    		if(follower_1_step_counter % (uint32_t)follower_1_int_part == 0)
    		{
    			follower_1_offset = follower_1_start_stop_speed * follower_1_dec_part;
    
    			// At this location, place the waitcnt for the time frame before
    			// this follower is supposed to make a step.
    			waitcnt(follower_1_offset + CNT);
    
    			// At this location, drive the follower's step pin high for the
    			// duration of this follower's pulse width (follower_1_pulse_width).
    			OUTA |= 1 << follower_1_step_pin;
    			waitcnt(follower_1_pulse_width + CNT);
    
    			// At this location, drive the follower's step pin low.
    			OUTA &= (~1 << follower_1_step_pin);
    
    			////////////////////////////////////////////////////////////
    			// This variable is only for testing purposes and it can be
    			// deleted, after testing is completed.
    			follower_1_steps_made++;
    			////////////////////////////////////////////////////////////
    
    			// The following timimg code needs adjustment to compensate for the
    			// if/else conditional statement, computations, the followers waitcnt
    			// offset, the drive high instruction, and the drive low instruction.
    			// The ultimate goal is to match the same time frame as utilized
    			// by the master's waitcnt.
    //			follower_1_start_stop_speed--;
    //			follower_1_start_stop_speed--;
    //			follower_1_start_stop_speed--;
    //			follower_1_start_stop_speed -= 3;
    //			follower_1_start_stop_speed = follower_1_start_stop_speed - 3;
    //			waitcnt((follower_1_start_stop_speed -= 3) + CNT);
    //			waitcnt((follower_1_start_stop_speed -= follower_1_ramp_inc_dec) + CNT);
    			waitcnt(follower_1_start_stop_speed + CNT);
    
    			// Decrement ramp_up_steps
    			follower_1_ramp_up_steps--;   
    		}
    		else
    		{
    			// The following timimg code needs adjustment to compensate for the
    			// if/else conditional statement, so that it will match the master's
    			// similar  waitcnt.
    			waitcnt(follower_1_start_stop_speed + CNT);
    //			waitcnt((follower_1_start_stop_speed -= follower_1_ramp_inc_dec) + CNT);
    
    			// Decrement ramp_up_steps
    			follower_1_ramp_up_steps--;
    		}
    	}
    
    	print("rampup complete\n");
    			
    	// Run this driver at speed consistent with the master's speed
    	while(follower_1_running_steps != 0)
    	{
    		// Pause for a high pulse on the master's step pin.
    		waitcnt(copy_1_masters_pulse_width + CNT);
    
    		// Maintain a step count consistent with the master's steps.
    		follower_1_step_counter++;
    
    		// Determine if the follower should make a step, if so, then make it.
    		// However keep in mind that all timing is critical at this point.
    		// So adjust timers appropriately
    		if(follower_1_step_counter % (uint32_t)follower_1_int_part == 0)
    		{
    			follower_1_offset = follower_1_start_stop_speed * follower_1_dec_part;
    
    			// At this location, place the waitcnt for the time frame before
    			// this follower is supposed to make a step.
    			waitcnt(follower_1_offset + CNT);
    
    			// At this location, drive the follower's step pin high for the
    			// duration of this follower's pulse width (follower_1_pulse_width).
    			OUTA |= 1 << follower_1_step_pin;
    			waitcnt(follower_1_pulse_width + CNT);
    
    			// At this location, drive the follower's step pin low.
    			OUTA &= (~1 << follower_1_step_pin);
    
    			////////////////////////////////////////////////////////////
    			// This variable is only for testing purposes and it can be
    			// deleted, after testing is completed.
    			follower_1_steps_made++;
    			////////////////////////////////////////////////////////////
    
    			// The following timimg code needs adjustment to compensate for the
    			// if/else conditional statement, computations, the followers waitcnt
    			// offset, the drive high instruction, and the drive low instruction.
    			// The ultimate goal is to match the same time frame as utilized
    			// by the master's waitcnt.
    			waitcnt(follower_1_start_stop_speed + CNT);
    
    			// Decrement running_steps
    			follower_1_running_steps--;
    		}
    		else
    		{       
    			// The following timimg code needs adjustment to compensate for the
    			// if/else conditional statement, so that it will match the master's
    			// similar  waitcnt.
    			waitcnt(follower_1_start_stop_speed + CNT);
    
    			// Decrement running_steps
    			follower_1_running_steps--;
    		}   
    	}
    
    	print("running complete\n");
    
    	// Maintain a ramp down consistent with the master's ramp up.
    	while(follower_1_ramp_down_steps != 0)
    	{
    		// Pause for a high pulse on the master's step pin.
    		waitcnt(copy_1_masters_pulse_width + CNT);
    
    		// Maintain a step count consistent with the master's steps.
    		follower_1_step_counter++;
    
    		// Determine if the follower should make a step, if so, then make it.
    		// However keep in mind that all timing is critical at this point.
    		// So adjust timers appropriately
    		if(follower_1_step_counter % (uint32_t)follower_1_int_part == 0)
    		{
    			follower_1_offset = follower_1_start_stop_speed * follower_1_dec_part;
    
    			// At this location, place the waitcnt for the time frame before
    			// this follower is supposed to make a step.
    			waitcnt(follower_1_offset + CNT);
    
    			// At this location, drive the follower's step pin high for the
    			// duration of this follower's pulse width (follower_1_pulse_width).
    			OUTA |= 1 << follower_1_step_pin;
    			waitcnt(follower_1_pulse_width + CNT);
    
    			// At this location, drive the follower's step pin low.
    			OUTA &= (~1 << follower_1_step_pin);
    
    			////////////////////////////////////////////////////////////
    			// This variable is only for testing purposes and it can be
    			// deleted, after testing is completed.
    			follower_1_steps_made++;
    			////////////////////////////////////////////////////////////
    
    			// The following timimg code needs adjustment to compensate for the
    			// if/else conditional statement, computations, the followers waitcnt
    			// offset, the drive high instruction, and the drive low instruction.
    			// The ultimate goal is to match the same time frame as utilized
    			// by the master's waitcnt.
    			waitcnt(follower_1_start_stop_speed + CNT);
    //			waitcnt((follower_1_start_stop_speed += follower_1_ramp_inc_dec) + CNT);
    
    			// Decrement ramp_down_steps
    			follower_1_ramp_down_steps--;
    		}
    		else
    		{
    			// The following timimg code needs adjustment to compensate for the
    			// if/else conditional statement, so that it will match the master's
    			// similar  waitcnt.
    			waitcnt(follower_1_start_stop_speed + CNT);
    //			waitcnt((follower_1_start_stop_speed += follower_1_ramp_inc_dec) + CNT);
    
    			// Decrement ramp_down_steps
    			follower_1_ramp_down_steps--;
    		}
    	}
    
    	print("rampdown complete\n");
    	print("follower_1_steps_made %u", follower_1_steps_made);
    }
    
  • The lowest possible "legal" value in Spin for waitcnt is about 385 - That's how long it takes to execute the waitcnt() spin instruction along with loading the CNT and adding the offset. Each additional thing you do adds processing time to the instruction, and increases the "minimum" number. Spin is not fast enough to use for the inner loop of this. You'd be better off using CMM compiled C, or LMM if you can afford the ram hit.
  • Jason

    Thanks for your input.

    Yes, I arrived at the 385 minimum about 4 hours ago
    Each additional thing you do adds processing time to the instruction, and increases the "minimum" number.

    I did not know that for a fact, but I suspected that and I have tried slightly larger numbers, but I will try higher.
    Spin is not fast enough to use for the inner loop of this.

    Hey now.... Similar code with addition and subtraction worked fine in Spin, but then I went to C with this code and then HALT :( However, I thought I had a similar section of code working under C when you and I were working on the 3D printer code, but I cannot find it anywhere. Maybe cause it never existed :)

    I have tried this code in both CMM and LMM, but to no avail.
  • I find it useful to create independent test programs for situations like this.

    Here's something that runs on the PAB:
    // PAB, C, LMM, Speed
    
    #include "simpletools.h"
    
    int main()
    {  
      long tixdelay = 33;
      
      high(26);
      waitcnt(tixdelay + CNT);
      high(27); 
    }
    

    If the delay is too low (which will cause a roll-over), the LEDs will not come on at [apparently] the same time. The low value will be dependent on compiler settings.

  • JonnyMac
    I find it useful to create independent test programs for situations like this.

    That is a darn FINE idea!!!! And sometimes I do that to, but I did not think of it this time. THANK YOU!

    When you say it runs on the PAB, does that mean you are sneaking in the 33 offset somehow or are you just using that as an example of a test for a too short delay?
  • Well this little snippet works fine
    #include "simpletools.h"
    
    int main()
    {
    	uint32_t follower_1_start_stop_speed = 14000;
    	uint32_t follower_1_ramp_inc_dec = 3;
    
    	waitcnt((follower_1_start_stop_speed -= follower_1_ramp_inc_dec) + CNT);
     
    	print("It works here");
    }
    
  • idbruce wrote: »
    As seen in the code below, follower_1_start_stop_speed starts out with a value of 14000. In it's current state, this code will run and output to the serial terminal, but if you try to change it to using one of the subtractions that have been commented out, it will hang during the first loop.

    The behaviour is really weird, I have added some debug output to see the values used by the various waitcnt and I don't see any obvious suspect value, aside from the 385 used by follower_1_pulse_width and copy_1_masters_pulse_width but should be enough in both LMM and CMM.

    The weird thing is that adding debug outputs also changes the behaviour. With your code it works as you describe and "hangs", but if I add print lines to see where it hangs, the program stops in another point. Seems that something corrupts the memory used by the waitcnt variables, I don't see anything that could do that (no pointers, no unusual variable usage, etc.).

    Just for testing I tried to change the floating point operations to integer and it works. I'm not sure if it is really that or it is just a side effect, but can you try to remove all floating point operations and see what happens ? follower_1_dec_part looks very precise but maybe the calculation could be rounded. Just for reference, I remember that the propeller gcc compiler had a bug with the floating point variables, it should have been fixed but the fix may not be included in the version used by SimpleIDE (if you are using it) (I'm using a recent version of GCC here so may not be that).

    Oh, follower_1_step_counter is uninitialized, should not make any difference, but better initialize all variables.
  • macca

    Thanks for taking a good look at it. It is really kind of mind boggling.
    The weird thing is that adding debug outputs also changes the behaviour.

    Yes and this is the third similar piece of code where a print statement can cause strange behavior.

    Thanks again for taking a look. Just imagine the GRIEF :)

    Any other brave souls want to take a crack at it?
  • macca

    You are correct...

    I have pin pointed it to the following code
    follower_1_offset = follower_1_start_stop_speed * follower_1_dec_part;
    
    // At this location, place the waitcnt for the time frame before
    // this follower is supposed to make a step.
    waitcnt(follower_1_offset + CNT);
    

    If I comment out the waitcnt(), then it will run, and if I uncomment it, and change follower_1_offset to a whole number, then it will run also. So it is the follower_1_offset that is the source of all my grief.
  • The solution is: waitcnt((uint32_t)follower_1_offset + CNT);

    Somehow the data type of follower_1_offset is being changed.
  • Okay I was wrong, it still has problems
  • Now this small section of test code works flawlessly, even if you alter the double follower_1_dec_part, it changes values just like it should, and the subtraction is working great. I just can't understand it.
    #include "simpletools.h"
    
    int main()
    {
      uint32_t follower_1_ramp_up_steps = 10;
      uint32_t follower_1_offset;
      uint32_t follower_1_int_part = 2;
      uint32_t follower_1_start_stop_speed = 14000;
      uint32_t follower_1_step_counter = 0;
      uint32_t follower_1_ramp_inc_dec = 3;
      double follower_1_dec_part = 0.6;
    
      while(follower_1_ramp_up_steps != 0)
      {
        follower_1_step_counter++;
    
        if(follower_1_step_counter % follower_1_int_part == 0)
        {
          follower_1_offset = follower_1_start_stop_speed * follower_1_dec_part;
    
          waitcnt(follower_1_offset + CNT);
    
          print("%u\n", follower_1_offset);
          
          follower_1_ramp_up_steps--;
          
          waitcnt((follower_1_start_stop_speed -= follower_1_ramp_inc_dec) + CNT);
        }
      }    
    }
    
  • Heater.Heater. Posts: 21,230
    Am I missing a point here or isn't this a very simple problem?

    CNT runs at 80 MHz, or whatever, regardless of what your program does.

    Your program, in whatever language, runs around at whatever instruction rate it does.

    If you order a wait on CNT. As in:
    waitcnt(follower_1_offset + CNT);
    

    Then either the count you ordered is coming up soon or it is not. If it is not you are hung for another total count around the counter.

    If you want to use waitcnt as a delay then you need to know what is the value of CNT now and calculate what value of CNT is in the future to complete the delay. And take account of the time it take your code to run whilst doing that.
  • Heater

    It is my understanding, that when a waitcnt is encountered, the program stops execution. If the waitcnt is too small, then the counter rolls over, and if it is a very large number, then you will be waiting a while. Either way, the program or cog should stop execution at waitcnt and wait for the time specified, regardless if the clock rolls around or just waits a long time. In other words, the program should not stop indefinitely.
  • I was wrong again....
    Now this small section of test code works flawlessly, even if you alter the double follower_1_dec_part, it changes values just like it should, and the subtraction is working great. I just can't understand it.

    It worked fine until you delete the print statement :(

    The following code will work flawlessly, until you delete the following print statement, at which point, the program will hang:
    print("%u\n", follower_1_offset);
    
    #include "simpletools.h"
    
    int main()
    {
      uint32_t follower_1_ramp_up_steps = 10;
      uint32_t follower_1_offset;
      uint32_t follower_1_int_part = 2;
      uint32_t follower_1_start_stop_speed = 14000;
      uint32_t follower_1_step_counter = 0;
      uint32_t follower_1_ramp_inc_dec = 3;
      double follower_1_dec_part = 0.6;
    
      while(follower_1_ramp_up_steps != 0)
      {
        follower_1_step_counter++;
    
        if(follower_1_step_counter % follower_1_int_part == 0)
        {
          follower_1_offset = follower_1_start_stop_speed * follower_1_dec_part;
    
          waitcnt(follower_1_offset + CNT);
          
          print("%u\n", follower_1_offset);
          
          follower_1_ramp_up_steps--;
          
          waitcnt((follower_1_start_stop_speed -= follower_1_ramp_inc_dec) + CNT);
        }
      }  
      print("rampup completed");   
    }
    
  • jmgjmg Posts: 15,148
    edited 2017-06-29 01:34
    idbruce wrote: »
    It is my understanding, that when a waitcnt is encountered, the program stops execution. If the waitcnt is too small, then the counter rolls over, and if it is a very large number, then you will be waiting a while. Either way, the program or cog should stop execution at waitcnt and wait for the time specified, regardless if the clock rolls around or just waits a long time. In other words, the program should not stop indefinitely.

    Are you saying the program freezes for longer than 2^32/SysCLK ?!

    I (and I think most) have assumed when you said 'hangs' you meant in real-time terms, and that it did resume 53.687s later ?

    If the program freezes totally >> 53s, you do not have a WAITCNT issue, rather, something has crashed/corrupted entirely.

    The worst possible outcome for true WAITCNT issues, is to roll over to the next equals.

    idbruce wrote: »
    The following code will work flawlessly, until you delete the following print statement, at which point, the program will hang:
    You should specify which exact tool chains/versions you are using here.

  • idbruceidbruce Posts: 6,197
    edited 2017-06-29 04:54
    jmg
    If the program freezes totally >> 53s, you do not have a WAITCNT issue, rather, something has crashed/corrupted entirely.

    It is definitely not a waitcnt issue, or as you would surmise after waiting several minutes.

    There are two print statements in the small test program above. With both print statements uncommented, the code works flawlessly as stated, but then if you comment out the first print statement, you would expect the second print statement to show up, but after several minutes, still no output.

    And as macca stated above, he had similar problems.....

    AND over in the General threads, about h-bridges, plau45 stated a similar experience with the print statements.

    I am using SimpleIDE Version 1.1.0

    macca said he was using GCC
  • I hereby dub this BIG BUG "Mothra" :)
  • idbruce wrote: »
    It is definitely not a waitcnt issue, or as you would surmise after waiting several minutes.

    I haven't tested the last snipped, but your original code worked after few minutes, you need to multiply the 53 seconds by the number of times the affected waitcnt is called and if I remember correctly it was called 6-7 times, so at least 5-6 minutes to wait.
    idbruce wrote: »
    I am using SimpleIDE Version 1.1.0

    macca said he was using GCC

    The bug I was referring to is this:
    http://forums.parallax.com/discussion/comment/1359250/

    AFAIK, it is fixed in the GCC version built here http://david.zemon.name:8111/project.html?projectId=PropGCC&tab=projectOverview&guest=1 but not in the gcc version shipped with SimpleIDE and I don't think that the double-to-uint32_t conversion is a workaround (I see that the site doesn't work, too bad, hope it is a temporary problem).

    My suggestion is still the same, can you try to remove all floating point operations so to be sure that you are not hitting that (or a similar) bug ?
  • idbruceidbruce Posts: 6,197
    edited 2017-06-29 07:09
    macca
    I haven't tested the last snipped, but your original code worked after few minutes, you need to multiply the 53 seconds by the number of times the affected waitcnt is called and if I remember correctly it was called 6-7 times, so at least 5-6 minutes to wait.

    Very interesting and you are correct. I just can't understand, why the print statement allows it to work, but if you comment it out, then it becomes compounded wait, based on the number of loops. This is some very crazy #$*&%.
    My suggestion is still the same, can you try to remove all floating point operations so to be sure that you are not hitting that (or a similar) bug ?

    Years ago, when I started working on several G-Code efforts, I came to the conclusion that it would be so much easier to just use floating point, if the use of memory and speed were within reason, which is one of the main reasons I switched to C from Spin. Now, just when I have something that I really want to sink my teeth into, the floating point is failing me. A complete and utter disappointment and I cannot believe this is a problem. This kind of bull should have been fixed a long time ago.
  • Try this instead:
    follower_1_offset = follower_1_start_stop_speed * 6 / 10;
    

    Your original code specified the mult as a double (64 bit float) not a float (32 bit). The values in the expression will all be promoted to double (cast up), then the mult will happen, and then the result will be cast back down to a uint32. It'll be horribly expensive on the Prop because float math in C runs in whatever mode you're compiling in (LMM or CMM), not as native PASM, and unless you tick the "32 bit doubles" box in there you're making it worse.

    He above version does the exact same work, but in integer math. As long as you're careful that the intermediate value won't exceed 32 bits this approach is fine, and preferable, because it'll be an order of magnitude faster.

    J
  • Jason

    Of course, that all seems fine and dandy for a hardcoded variable used for testing, but this goes much deeper than one hard coded variable.

    And of course, I would like to use as much of my previous code that I possibly can. The use of doubles, goes back a long way into my current and past efforts.

    It all starts with the G-Code struct, that was intended to be passed from one Propeller to another.

    I am sure you remember this structure and what I am referring to. This structure is inter-mingled through so much code, that it would take a very long time to switch it all over:
    #ifndef _GCODE_STRUCT_H
    #define _GCODE_STRUCT_H
    
    #include <stdint.h>
    
    /// Structure for holding GCODE fileline data
    typedef struct
    {
    	char valid[10];
    	char gcode_fileline[100];
    
    	char g_char[3];
    	char m_char[3];
    	char t_char[1];
    	char s_char[5];
    	char p_char[5];
    	char x_char[9];
    	char y_char[8];
    	char z_char[8];
    	char e_char[8];
    	char f_char[8];
     
    	uint8_t g_num;
    	uint8_t m_num;
    	uint8_t t_num;
    	uint16_t s_num;
    	uint16_t p_num;
    	double x_num;
    	double y_num;
    	double z_num;
    	double e_num;
    	double f_num;
    
    	double x_travel;
    	double y_travel;
    	double z_travel;
    	double e_travel;
    
    	uint8_t x_dir;
    	uint32_t x_start_stop_speed;
    	uint32_t x_total_steps;
    //	uint32_t x_ramp_up_steps;
    //	uint32_t x_running_steps;
    //	uint32_t x_ramp_down_steps;
    //	uint32_t x_ramp_inc_dec;
    
    	uint8_t y_dir;
    	uint32_t y_start_stop_speed;
    	uint32_t y_total_steps;
    //	uint32_t y_ramp_up_steps;
    //	uint32_t y_running_steps;
    //	uint32_t y_ramp_down_steps;
    //	uint32_t y_ramp_inc_dec;
    
    	uint8_t z_dir;
    	uint32_t z_start_stop_speed;
    	uint32_t z_total_steps;
    //	uint32_t z_ramp_up_steps;
    //	uint32_t z_running_steps;
    //	uint32_t z_ramp_down_steps;
    //	uint32_t z_ramp_inc_dec;
    
    	uint8_t e_dir;
    	uint32_t e_start_stop_speed;
    	uint32_t e_total_steps;
    //	uint32_t e_ramp_up_steps;
    //	uint32_t e_running_steps;
    //	uint32_t e_ramp_down_steps;
    //	uint32_t e_ramp_inc_dec;
    
    } GCODE_STRUCT;
    
    /// Structures for holding the current and
    /// previous GCODE fileline data.
    extern volatile GCODE_STRUCT current_gcode;
    extern GCODE_STRUCT previous_gcode;
    
    #endif /* _GCODE_STRUCT_H */
    
  • idbruceidbruce Posts: 6,197
    edited 2017-06-29 08:29
    Actually, if I back track a little, I suppose I could make something like that work, if I can take that principle back to this computation and others just like it, then all my other code that "seems" to be working, could remain as is, and this would be the only portion affected.
    current_move.follower_1_ratio = (double)current_move.master_total_steps / 
    	(double)current_gcode.y_total_steps;
    
    current_move.follower_1_int_part = (uint32_t)current_move.follower_1_ratio;
    
    current_move.follower_1_dec_part = current_move.follower_1_ratio - 
    	current_move.follower_1_int_part;
    
  • idbruceidbruce Posts: 6,197
    edited 2017-06-29 09:20
    For those that may be inclined to get a glimpse of the actual code where the problem exists, I am including a copy of my current experiment, in which I am attempting to synchronize 4 axes of motion, with 4 different cogs, in a master/follower relationship, associated with common gearing. To learn more about this experiment, you should visit this thread: forums.parallax.com/discussion/166880/cnc-i-may-not-be-crazy-after-all-multi-axis-stage-synchronization

    Please excuse the mess within the project, because it is a work in progress, with quite a bit of redundancy.
  • idbruce wrote: »
    Years ago, when I started working on several G-Code efforts, I came to the conclusion that it would be so much easier to just use floating point, if the use of memory and speed were within reason, which is one of the main reasons I switched to C from Spin. Now, just when I have something that I really want to sink my teeth into, the floating point is failing me. A complete and utter disappointment and I cannot believe this is a problem. This kind of bull should have been fixed a long time ago.

    I agree with you, however we are trying to debug a problem and converting to integer math takes some things out of the equation.
    There is definitely something wrong here, could be the floating point operation, could be the version of gcc used by SimpleIDE, could be the options used by the compiler, or a combination of things.

    I tried to simplify the things a bit more getting rid of simplelibrary, the results are interesting.
    With floating point, LMM works, CMM doesn't work. Looks like some value gets too small for waitcnt, if I change follower_1_start_stop_speed to 24000 it works, so looks like the calculated value is too small for CMM which is the default mode for SimpleIDE.
    With integer math, LMM and CMM works, so, unless I did something plain wrong, the initial values are correct and the resulting calculations are still correct after all.

    What does this means ? Floating point math is wrong in CMM ?
    Or maybe there is something else that we don't see.

    If you want to experment with command line, I'm attaching a simple Makefile project that doesn't use simplelibrary and uses its own uart driver, this should take any library issue out of the equation.
  • macca

    It has been a while since I tested with LMM and since you mentioned it, I figured I would give it a try with that small test snippet above.

    With the first "print" commented out and the project built in LMM, it ran just fine, as long as the number of ramps did not exceed 2100. Then I uncommented the print statement to see what the numbers were looking like and it locked up just after printing the follower_1_offset value of 4753.

    It is all very interesting, but very aggravating.
  • idbruceidbruce Posts: 6,197
    edited 2017-06-29 13:51
    Okay Jason...

    Going back to what you said earlier and how I responded...

    I posted this snippet:
    current_move.follower_1_ratio = (double)current_move.master_total_steps / 
    	(double)current_gcode.y_total_steps;
    
    current_move.follower_1_int_part = (uint32_t)current_move.follower_1_ratio;
    
    current_move.follower_1_dec_part = current_move.follower_1_ratio - 
    	current_move.follower_1_int_part;
    

    All three of the structure members being initialized above, are all currently double data types. I suppose I could eliminate current_move.follower_1_ratio completely, and I could change current_move.follower_1_int_part and current_move.follower_1_dec_part to 32 bit unsigned integers. Then I am thinking that I could replace the code above, with something similar to this:
    current_move.follower_1_int_part = current_move.master_total_steps / 
    	current_gcode.y_total_steps;
    
    current_move.follower_1_dec_part = (current_move.master_total_steps % 
    	current_gcode.y_total_steps) * 10000;
    

    and then, going in line with what you said, in the problem area:
    follower_1_offset = follower_1_start_stop_speed * follower_1_dec_part  / 10000;
    

    Does that sound and look right?
  • It does, but scaling it up like that is going to blow over the 32 bit number limit relatively easily. You may need to split it into an upper / lower half for it to work for the range of values you want. You could lower the scale down to 8 bits (256) if that gives you enough range. That'd be roughly 16 million steps, or 8 million signed.

    Your print is killing the code because they take *forever* in Prop land. Comment out the waitcnt and run the loop with the print to see what the values would have been.
  • macca wrote: »
    With floating point, LMM works, CMM doesn't work. Looks like some value gets too small for waitcnt, if I change follower_1_start_stop_speed to 24000 it works, so looks like the calculated value is too small for CMM which is the default mode for SimpleIDE.
    With integer math, LMM and CMM works, so, unless I did something plain wrong, the initial values are correct and the resulting calculations are still correct after all.

    What does this means ? Floating point math is wrong in CMM ?
    Or maybe there is something else that we don't see.

    Could it be that CMM is just too slow for the values used? I previously ran some tests looking at how long it take for the prop to switch a pin from output high to low. I compared different languages (PASM, Spin, Prop C in LMM and CMM, pfth) and different methods of making the switch. I used one of the counters to count the number of clocks from issuing the command to the time the pin went low.

    When using C, changing nothing but the memory model, CMM was about 5 times slower than LMM.

    For example when using the function low(pinnum) from SimpleTools LMM took129 clocks and CMM took 689 clocks. (PASM took 6 clocks). Note that using the Propeller.h library instead of SimpleTools functions reduced the times greatly for both CMM and LMM.

    All the results are shown in this thread:
    https://forums.parallax.com/discussion/164271/solved-with-timing-results-problem-with-pasm-counters-ctra

    Tom

Sign In or Register to comment.