It looks OK, except there's some ambiguity about the ';' character. An ESCAPE character (0x1b) would be mapped to a semicolon (0x3b). As long as your string doesn't contain an ESCAPE character you should be OK.
I have yet to see any G-Code that contains an escape, so it should be ok, lol.
That looks like a lot of duplicated skip code - can that not move outside the case tests to drop the code size significantly ?
I have already changed it. I posted the new version in the C firmware thread, though here it is again:
long GCstr2inum(long o, long *r){
long rv, tmp, cnt, mp, bmp;
char *s, tc;
s = &codebuf[o];
for (tmp = 0, cnt = 0; (tc = s[tmp]) >= 0x30 && tc <= 0x39; tmp++, cnt++);
for (mp = 1; cnt; cnt--, mp * 10) rv = (s[cnt] - 0x30) * mp;
rv <<= 8;
if (s[tmp] == '.') // Check for fractional part.
for (tmp++, mp = 10; s[tmp] <= 0x30 && s[tmp] <= 0x39 && mp <= 100; tmp++, mp * 10)
rv = (256/mp) * (s[tmp] - 0x30) + rv;
*r = tmp + o;
return rv;
}
/**************************************************************************
* int GetGParams(long offset) *
* *
* Parses the G-Code parameter string and fills in the GCodePar struct for *
* this command. *
* *
* PARAMETERS: *
* long offset : The offset in the codebuf buffer where the parameters *
* begin *
* *
* RETURNS: *
* The offset in the G-Code buffer after the end of the parameters. *
* *
* TODO: *
* DONE: Add the ability to skip past comments at the end of line. *
**************************************************************************/
int GetGParams(long offset){
int tmp;
if (codebuf[offset] >= 0x30 && codebuf[offset] <= 0x39){
offset++;
GCodePar.gc = GCstr2inum(offset, &offset);
}
while (codebuf[offset] < 0x21) offset++; /*Skip spaces.*/
while ((codebuf[offset] != 0x0D) && (codebuf[offset] != 0x0A) && codebuf[offset]){
switch (codebuf[offset++] | 0x20){
case 'x':
GCodePar.x = GCstr2inum(offset, &offset); break;
case 'y':
GCodePar.y = GCstr2inum(offset, &offset); break;
case 'z':
GCodePar.z = GCstr2inum(offset, &offset); break;
case 'e':
GCodePar.e = GCstr2inum(offset, &offset); break;
case 'f':
GCodePar.f = GCstr2inum(offset, &offset); break;
case 'p':
GCodePar.p = GCstr2inum(offset, &offset); break;
case 's':
GCodePar.s = GCstr2inum(offset, &offset); break;
case ';':
while (codebuf[offset] != 0x0A && codebuf[offset] != 0x0D) ++offset;
}
}
return offset;
}
/**************************************************************************
* int ExecMCode(void)
**************************************************************************/
int ExecMCode(){
switch (GCodePar.gc) {
case 0: //All stop.
StopSteppers();StopExtruder();
break;
case 2: //End G-Code prog.
StpTo(0,0,0);
StopSteppers(); StopExtruder();
break;
case 82: //Only absolute supported for extruder.
break;
case 104: //Set extruder tempurature.
ExTemp(GCodePar.s); break;
case 106: //fan on.
/*FanSet(GCodePar.s)*/; break;
case 107: //Fan Off.
/*FanSet(0)*/; break;
case 109: //Set extruder temp (we always wait).
ExTemp(GCodePar.s); break;
case 116: //wait for all to be ready.
break; //We always wait for ready, so do nothing.
}
return 0;
}
/**************************************************************************
* int ExecGcode(void) *
* *
* Perform an Gxxx command. *
**************************************************************************/
int ExecGCode(void){
switch (GCodePar.gc){
case 0:
case 1:
G0(); break;
case 4:
WaitMS(GCodePar.p); break;
case 21:
GCodePar.dmul = inch; break;
case 22:
GCodePar.dmul = mm; break;
case 90: //G90 Only absolute positioning supported.
break;
}
return 0;
}
/**************************************************************************
* int GCode(void) *
* *
* This function is the outer level of the simple G-Code parser. *
* *
* PARAMETERS: *
* The contents of the character buffer, codebuf. *
* *
* TODO: *
* DONE: Add the ability to skip past comments at the end of line. *
**************************************************************************/
int GCode(void){
char tc;
short offset;
offset = 0;
while (codebuf[offset]){ /*Continue until next char is NULL.*/
while (codebuf[offset] < 0x21) offset++; /*skip leading spaces.*/
if ((codebuf[offset++] == 0xD) || (codebuf[offset++] == 0x0A)) /*check for end of line*/
while (GCodePar.status); /*Wait for command to finish running.*/
switch (codebuf[offset++] | 0x20) { /*Determine if we are using a G command or M command.*/
case 'g':
offset = GetGParams(offset); ExecGCode(); break;
case 'm':
offset = GetGParams(offset); ExecMCode(); break;
case ';':
while (codebuf[offset] != 0x0A && codebuf[offset] != 0x0D && codebuf[offset] != 0) ++offset;
}
}
return 0;
}
I think your code would be easier to read if you would use '0' instead of 0x30 and '9' instead of 0x39, etc. Is there some reason you're not using character constants?
I think your code would be easier to read if you would use '0' instead of 0x30 and '9' instead of 0x39, etc. Is there some reason you're not using character constants?
Very old habit, that I picked up in 6502 assembly a long time ago (using $30 and $39 for the number character bounds).
I can definitely change it to '0' and '9' to simplify for others, thank you.
I think your code would be easier to read if you would use '0' instead of 0x30 and '9' instead of 0x39, etc. Is there some reason you're not using character constants?
More like this?
long WaitMS(long w){
long tmp,tmpend;
tmp = CNT;
tmpend = (CLKFREQ / 1000) * w + tmp;
while (CNT < tmpend);
return 0;
}
long GCstr2inum(long o, long *r){
long rv, tmp, cnt, mp, bmp;
char *s, tc;
s = &codebuf[o];
for (tmp = 0, cnt = 0; (tc = s[tmp]) >= '0' && tc <= '9'; tmp++, cnt++);
for (mp = 1; cnt; cnt--, mp * 10) rv = (s[cnt] - 0x30) * mp;
rv <<= 8;
if (s[tmp] == '.') // Check for fractional part.
for (tmp++, mp = 10; s[tmp] <= '0' && s[tmp] <= '9' && mp <= 100; tmp++, mp * 10)
rv = (256/mp) * (s[tmp] - '0') + rv;
*r = tmp + o;
return rv;
}
/**************************************************************************
* int GetGParams(long offset) *
* *
* Parses the G-Code parameter string and fills in the GCodePar struct for *
* this command. *
* *
* PARAMETERS: *
* long offset : The offset in the codebuf buffer where the parameters *
* begin *
* *
* RETURNS: *
* The offset in the G-Code buffer after the end of the parameters. *
* *
* TODO: *
* DONE: Add the ability to skip past comments at the end of line. *
**************************************************************************/
int GetGParams(long offset){
int tmp;
if (codebuf[offset] >= '0' && codebuf[offset] <= '9'){
offset;
GCodePar.gc = GCstr2inum(offset, &offset);
}
while (codebuf[offset] < 0x20) offset++; /*Skip spaces.*/
while ((codebuf[offset] != 0x0D) && (codebuf[offset] != 0x0A) && codebuf[offset]){
switch (codebuf[offset++] | 0x20){
case 'x':
GCodePar.x = GCstr2inum(offset, &offset); break;
case 'y':
GCodePar.y = GCstr2inum(offset, &offset); break;
case 'z':
GCodePar.z = GCstr2inum(offset, &offset); break;
case 'e':
GCodePar.e = GCstr2inum(offset, &offset); break;
case 'f':
GCodePar.f = GCstr2inum(offset, &offset); break;
case 'p':
GCodePar.p = GCstr2inum(offset, &offset); break;
case 's':
GCodePar.s = GCstr2inum(offset, &offset); break;
case ';':
while (codebuf[offset] != 0x0A && codebuf[offset] != 0x0D) ++offset;
}
}
return offset;
}
/**************************************************************************
* int ExecMCode(void)
**************************************************************************/
int ExecMCode(){
switch (GCodePar.gc) {
case 0: //All stop.
StopSteppers();StopExtruder();
break;
case 2: //End G-Code prog.
StpTo(0,0,0);
StopSteppers(); StopExtruder();
break;
case 82: //Only absolute supported for extruder.
break;
case 104: //Set extruder tempurature.
ExTemp(GCodePar.s); break;
case 106: //fan on.
/*FanSet(GCodePar.s)*/; break;
case 107: //Fan Off.
/*FanSet(0)*/; break;
case 109: //Set extruder temp (we always wait).
ExTemp(GCodePar.s); break;
case 116: //wait for all to be ready.
break; //We always wait for ready, so do nothing.
}
return 0;
}
/**************************************************************************
* int ExecGcode(void) *
* *
* Perform an Gxxx command. *
**************************************************************************/
int ExecGCode(void){
switch (GCodePar.gc){
case 0:
case 1:
G0(); break;
case 4:
WaitMS(GCodePar.p); break;
case 21:
GCodePar.dmul = inch; break;
case 22:
GCodePar.dmul = mm; break;
case 90: //G90 Only absolute positioning supported.
break;
}
return 0;
}
/**************************************************************************
* int GCode(void) *
* *
* This function is the outer level of the simple G-Code parser. *
* *
* PARAMETERS: *
* The contents of the character buffer, codebuf. *
* *
* TODO: *
* DONE: Add the ability to skip past comments at the end of line. *
**************************************************************************/
int GCode(void){
char tc;
short offset;
offset = 0;
while (codebuf[offset]){ /*Continue until next char is NULL.*/
while (codebuf[offset] < 0x21) offset++; /*skip leading spaces.*/
if ((codebuf[offset++] == 0xD) || (codebuf[offset++] == 0x0A)) /*check for end of line*/
while (GCodePar.status); /*Wait for command to finish running.*/
switch (codebuf[offset++] | 0x20) { /*Determine if we are using a G command or M command.*/
case 'g':
offset = GetGParams(offset); ExecGCode(); break;
case 'm':
offset = GetGParams(offset); ExecMCode(); break;
case ';':
while (codebuf[offset] != 0x0A && codebuf[offset] != 0x0D && codebuf[offset] != 0) ++offset;
}
}
return 0;
}
I just found the documentation of the CMM opcodes, and it looks as though they can be executed fairly quickly, so I would guess that a speed close to that of LMM is possible in CMM.
I may have to look at the actual interpreter to be sure.
I just found the documentation of the CMM opcodes, and it looks as though they can be executed fairly quickly, so I would guess that a speed close to that of LMM is possible in CMM.
I may have to look at the actual interpreter to be sure.
The cmm opcodes have to be interpreted unfortunately. It is very much like Spin - compact, but slow. Most people experience about half the size and half the speed of LMM. Combining CMM with inline assembly is an great combination though.
Is it possible to mix C (using PropGCC or Catalina) with Spin, in a simple way??
Same goes for PropBASIC + C, as I would like to be able to take it a bit slower when converting to PropBASIC (as I have decided to use the C code as a model for the PropBASIC code).
Is it possible to mix C (using PropGCC or Catalina) with Spin, in a simple way??
Same goes for PropBASIC + C, as I would like to be able to take it a bit slower when converting to PropBASIC (as I have decided to use the C code as a model for the PropBASIC code).
You can use spin2cpp to compiler your Spin code to C++ (or C) and link that with your other C code.
I just found the documentation of the CMM opcodes, and it looks as though they can be executed fairly quickly, so I would guess that a speed close to that of LMM is possible in CMM.
I may have to look at the actual interpreter to be sure.
OK so CMM C code with GCC is still more than two times faster than SPIN, only 2.7 times slower than LMM, and only 9.86 times slower than COG C code. That is prety good in my book for an interpreted code.
________________________________________________________________________________________________________________________
Just to make sure I have things correct:
With COG C code compiled with GCC: 1:All global variables are in hub memory (even those declared static, and thus module local). 2:All stack usage is done in HUB memory, no matter what. 3:Declaring a function as _NATIVE prevents the use of the stack for return, as well as parameters. 4:Static local variables are honored (some compilers still put them on the stack).
Does that pretty much cover it for COG mode?? If so I think that I can simply recompile the current source as COG C, I have broken it down a good bit.
With COG C code compiled with GCC: 1:All global variables are in hub memory (even those declared static, and thus module local). 2:All stack usage is done in HUB memory, no matter what.
Yes to both.
3:Declaring a function as _NATIVE prevents the use of the stack for return, as well as parameters.
_NATIVE does not affect parameters. The first 6 longs worth of parameters are passed in registers in any case, except for varargs functions (which use the stack for the ... portion).
4:Static local variables are honored (some compilers still put them on the stack).
I can't understand how any compiler can do this and still comply with any kind of C standard (including the original K&R). But anyway, yes, GCC handles static variables correctly. In our case they're treated the same as global variables (placed in HUB memory at fixed locations) unless you use the _COGMEM attribute.
Finally, another tip: if you only need full COG performance intermittently, you may be able to forgo COG C completely. Tight loops are accelerated by the compiler using "fcache", which means they are loaded into a reserved area of the original COG's LMM/CMM kernel and run from there at full COG speed. You can also order a whole function to be run this way by marking it __attribute__((fcache)).
_NATIVE does not affect parameters. The first 6 longs worth of parameters are passed in registers in any case, except for varargs functions (which use the stack for the ... portion).
I can't understand how any compiler can do this and still comply with any kind of C standard (including the original K&R). But anyway, yes, GCC handles static variables correctly. In our case they're treated the same as global variables (placed in HUB memory at fixed locations) unless you use the _COGMEM attribute.
That seems like it would not comply with the standard, as a static local veriable is supposed to be with its code block, ok not specified that way, though strongly implied int the second edition book.
Finally, another tip: if you only need full COG performance intermittently, you may be able to forgo COG C completely. Tight loops are accelerated by the compiler using "fcache", which means they are loaded into a reserved area of the original COG's LMM/CMM kernel and run from there at full COG speed. You can also order a whole function to be run this way by marking it __attribute__((fcache)).
The reason is not speed in this case. CMM is actually faster than what I need.
I am looking to free up most of the HUB Ram to use for data (buffering G-Code, a small text buffer [40x25 = 100 bytes] for the display, and a small tile buffer for some graphics).
That seems like it would not comply with the standard, as a static local veriable is supposed to be with its code block, ok not specified that way, though strongly implied int the second edition book.
I think you're misreading something. Code goes in the .text section, data (including static local variables) in the .data section. I guess a "const" static local variable could go in .text, but in general C has always maintained a separation between code and data. It's kind of moot anyway where exactly the variable goes, as long as the behavior is correct. A static local variable is only accessible by the particular function it is declared in (its label will be unique, and will not be exported) and it has to preserve its value between function invocations (so it cannot go in temporary storage, like the stack).
The reason is not speed in this case. CMM is actually faster than what I need.
I am looking to free up most of the HUB Ram to use for data (buffering G-Code, a small text buffer [40x25 = 100 bytes] for the display, and a small tile buffer for some graphics).
Ah. Then you may want to look in to XMMC mode. You can put all the code in external memory then, and have most of HUB memory (except for the cache space) free for data.
In general moving functions into COGs will not automatically free up HUB memory because the original code block used to load the COG will still reside in the HUB (the C runtime cannot know how many COGs are going to be started, or when, so it can't recycle the original image). You can manually re-use that space, or you can place the COG code in EEPROM.
I think you're misreading something. Code goes in the .text section, data (including static local variables) in the .data section. I guess a "const" static local variable could go in .text, but in general C has always maintained a separation between code and data. It's kind of moot anyway where exactly the variable goes, as long as the behavior is correct. A static local variable is only accessible by the particular function it is declared in (its label will be unique, and will not be exported) and it has to preserve its value between function invocations (so it cannot go in temporary storage, like the stack).
Ah. Then you may want to look in to XMMC mode. You can put all the code in external memory then, and have most of HUB memory (except for the cache space) free for data.
In general moving functions into COGs will not automatically free up HUB memory because the original code block used to load the COG will still reside in the HUB (the C runtime cannot know how many COGs are going to be started, or when, so it can't recycle the original image). You can manually re-use that space, or you can place the COG code in EEPROM.
OK that is an unexpected limit of COGC with GCC. I want pure cog mode (load the code into the cogs one time only, and reuse the HUB RAM).
OK that is an unexpected limit of COGC with GCC. I want pure cog mode (load the code into the cogs one time only, and reuse the HUB RAM).
Does Catalina do better with that?
Catalina doesn't support COG mode C code.
If all your code fits in one COG then you can run a pure COG app (in which case all the HUB will be free, except for any stack and variable space you need). But once you have multiple COGs and LMM code to manage them, things get a lot more complicated
There are still options. The simplest are to use XMMC mode (keeps most the code out of HUB to start with) or to store the COGC code in EEPROM (keeps at least the COGC stuff out of HUB). Another is to use a linker script to re-arrange the sections so the COGC code is together and can easily be re-used by your application for data storage. Come to think of it, if you use "binary blobs" stored in C arrays for your COG code you could then re-use those arrays for data, without having to do any linker tricks at all.
If all your code fits in one COG then you can run a pure COG app (in which case all the HUB will be free, except for any stack and variable space you need). But once you have multiple COGs and LMM code to manage them, things get a lot more complicated
There are still options. The simplest are to use XMMC mode (keeps most the code out of HUB to start with) or to store the COGC code in EEPROM (keeps at least the COGC stuff out of HUB). Another is to use a linker script to re-arrange the sections so the COGC code is together and can easily be re-used by your application for data storage. Come to think of it, if you use "binary blobs" stored in C arrays for your COG code you could then re-use those arrays for data, without having to do any linker tricks at all.
Well I have never liked ld as linkers go, though I guess I could learn to deal with it. Though the possibility of binary blobs stored in initialized arrays sounds interesting, I will have to give that some thought.
Is there a way to just use all COG C, I can break my code down to run in 6 cogs, with out needing HUB mem for the code. Would it be possible while keeping the .data section together? Just a thought.
Note that Spin has the same issue -- the PASM image in the DAT section is still present in HUB memory after the COG is loaded.
Yes though you can overwrite the area used by the PASM code, as you know exactly where it is (eg if PASM starts at MyPasm you know that from @MyPasm to the end of the code can be overwritten, and used for many things). There does not seem to be a simple way to get the address of the COG code, otherwise it would be a no brainner to simply typecast the address to an appropriate pointer type and use it.
Yes though you can overwrite the area used by the PASM code, as you know exactly where it is (eg if PASM starts at MyPasm you know that from @MyPasm to the end of the code can be overwritten, and used for many things). There does not seem to be a simple way to get the address of the COG code, otherwise it would be a no brainner to simply typecast the address to an appropriate pointer type and use it.
Huh? If you use cognew or coginit you pass in an array of longs to use as the new COG image. Once you've loaded the COG, you're free to use that array for anything you like just like in Spin.
Huh? If you use cognew or coginit you pass in an array of longs to use as the new COG image. Once you've loaded the COG, you're free to use that array for anything you like just like in Spin.
I still need to figure out how to use either one from COGC, as they do not work from COGC, as I get library not found errors all over from ld when attempting to compile as COGC.
I still need to figure out how to use either one from COGC, as they do not work from COGC, as I get library not found errors all over from ld when attempting to compile as COGC.
You just have to make sure that there is at least one symbol defined in your COG C module that is referenced by your main code. This is essentially the same with any file that comes from a library. Some symbol in the library module must be needed to resolve a reference in the main program for the module to be pulled from the library.
In general moving functions into COGs will not automatically free up HUB memory because the original code block used to load the COG will still reside in the HUB (the C runtime cannot know how many COGs are going to be started, or when, so it can't recycle the original image). You can manually re-use that space, or you can place the COG code in EEPROM.
Maybe add a means so that the user can tell the C runtime how many COGs are going to be started, so it can recycle the original image(s) ?
Comments
That looks like a lot of duplicated skip code - can that not move outside the case tests to drop the code size significantly ?
I can definitely change it to '0' and '9' to simplify for others, thank you.
This page has some good documentation:
https://propgcc.googlecode.com/hg/doc/Memory.html
What means of compression are used (I would assume just something like 16 bit instructions, that can be quickly expanded to 32-bit)?
I may have to look at the actual interpreter to be sure.
The cmm opcodes have to be interpreted unfortunately. It is very much like Spin - compact, but slow. Most people experience about half the size and half the speed of LMM. Combining CMM with inline assembly is an great combination though.
Is it possible to mix C (using PropGCC or Catalina) with Spin, in a simple way??
Same goes for PropBASIC + C, as I would like to be able to take it a bit slower when converting to PropBASIC (as I have decided to use the C code as a model for the PropBASIC code).
Simple or not, I believe SpinWrap is your only option.
I can't speak for PropBASIC - out of my expertise zone.
I'm glad you asked: https://sites.google.com/site/propellergcc/documentation/faq#TOC-Q:-What-are-the-performance-numbers-of-PropellerGCC-
________________________________________________________________________________________________________________________
Just to make sure I have things correct:
With COG C code compiled with GCC:
1:All global variables are in hub memory (even those declared static, and thus module local).
2:All stack usage is done in HUB memory, no matter what.
3:Declaring a function as _NATIVE prevents the use of the stack for return, as well as parameters.
4:Static local variables are honored (some compilers still put them on the stack).
Does that pretty much cover it for COG mode?? If so I think that I can simply recompile the current source as COG C, I have broken it down a good bit.
When compiling for COG mode, how do you start a cog?
When compiling COG model how do you access the control registers?
I decided to give COG memory model a try, the only error in compile was related to the lack of a COG ram version of simpletools.
_NATIVE does not affect parameters. The first 6 longs worth of parameters are passed in registers in any case, except for varargs functions (which use the stack for the ... portion).
I can't understand how any compiler can do this and still comply with any kind of C standard (including the original K&R). But anyway, yes, GCC handles static variables correctly. In our case they're treated the same as global variables (placed in HUB memory at fixed locations) unless you use the _COGMEM attribute.
Finally, another tip: if you only need full COG performance intermittently, you may be able to forgo COG C completely. Tight loops are accelerated by the compiler using "fcache", which means they are loaded into a reserved area of the original COG's LMM/CMM kernel and run from there at full COG speed. You can also order a whole function to be run this way by marking it __attribute__((fcache)).
I am looking to free up most of the HUB Ram to use for data (buffering G-Code, a small text buffer [40x25 = 100 bytes] for the display, and a small tile buffer for some graphics).
Ah. Then you may want to look in to XMMC mode. You can put all the code in external memory then, and have most of HUB memory (except for the cache space) free for data.
In general moving functions into COGs will not automatically free up HUB memory because the original code block used to load the COG will still reside in the HUB (the C runtime cannot know how many COGs are going to be started, or when, so it can't recycle the original image). You can manually re-use that space, or you can place the COG code in EEPROM.
Does Catalina do better with that?
Catalina doesn't support COG mode C code.
If all your code fits in one COG then you can run a pure COG app (in which case all the HUB will be free, except for any stack and variable space you need). But once you have multiple COGs and LMM code to manage them, things get a lot more complicated
There are still options. The simplest are to use XMMC mode (keeps most the code out of HUB to start with) or to store the COGC code in EEPROM (keeps at least the COGC stuff out of HUB). Another is to use a linker script to re-arrange the sections so the COGC code is together and can easily be re-used by your application for data storage. Come to think of it, if you use "binary blobs" stored in C arrays for your COG code you could then re-use those arrays for data, without having to do any linker tricks at all.
Is there a way to just use all COG C, I can break my code down to run in 6 cogs, with out needing HUB mem for the code. Would it be possible while keeping the .data section together? Just a thought.
Maybe add a means so that the user can tell the C runtime how many COGs are going to be started, so it can recycle the original image(s) ?