Hello.
I am a big fan of the SubRoutine and tend to make my program up using (lots of) small repeated sections of code placed into Subs or Functions.
I read in my datasheet for the 16F1829 that it has a 16 level stack. I have read earlier on the forum that some of the GCB LCD and Print routines can add a few levels of 'Stack space' to the program, but how can I ensure that my programming hasn't caused (or potentially could cause) a Stack overflow?
I also read, having searched the forum for 'Stack' that I can look in the .lst file and count the stack 'Push and Pop' numbers. I have looked in the .lst file and can't find any references to either Stack, Push or Pop.
I've just been trying to build a flowchart for my program, in an attempt to track down each 'Sub' call, from within each SubRoutine. So far, it looks as though my own stack depth is no greater than 7 so I think I should be under the 16 level limit.
Is there a way of finding the Stack level from the .lst file (or any of the other files GCB creates)?
Can anyone suggest a plugin for SynWrite that can show the SubRoutine nesting?
Or am I stuck to running through the code and counting the Sub calls manually?
Last edit: mkstevo 2017-10-03
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
PIC Processors are Harvard Architecture.
That means that they have separate Address and Data Busses.
It also means that they have no Stack Pointe.
In its place they have a fixed stack, the size of which varies from device to device.
As a result would not be able to easily, if at all, read the address and place it in a user stack, even if you manually maintained two registers as a stack pointer.
That is the main reason why there were no C Compilers for PIC in the early decades.
It is also a testament to Hugh’s tenacity in tackling a compiler for PIC Processors in the first place and his Genius for having pulled it off.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks Chris. I think I'm still using z80 and 6502. My dumbness was continued with picaxe that has push and pop which is not good teaching.
Lots not to assume in GCB.
"That is the main reason why there were no C Compilers for PIC in the early decades." ??
The basic compilers I used in the 80's were 2 pass. 1st labelled addresses,2nd actually defined the addresses. Beyond me.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Well, I've put some thought into this, and haven't really got terribly far, but here is what I have come up with so far.
I wrote an AppleScript that reads the GCB source code, it determines the name of the SubRoutines and Functions. It then distils each SubRoutine into only it's external calls. This is written to a text file.
As ever, actions speak louder than words, so here is the result of the script on a trivial program:
Now that is great, I can see the SubRoutine calls. What I'd ideally like is to take this a step further, and incorporate the nested sub calls. So I end up with something like this:
So I can now see that the call to CaptureData results in a maximum stack depth of three (1.CatureData > 2.OutputData > 3.Wipe_LineArray). In this program it isn't difficult to track, but in the program I'm wrestling with, it is rather more difficult as it contains forty+ SubRoutines.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
My next thought was to borrow from a program I wrote in Borland Delphi some years ago that captured keyboard and mouse events. This became impossible to trace in the usual way, as each time an event fires, a new event is generated, and as the trace debugger displays - it fires an event... So, for that I wrote an external program that listened to messages from the main executable, and wrote these to a text file. Each SubRoutine had an entry message and at each exit point an exit message. The Debugging program could then time each SubRoutine, and show the routine calls.
At the start of each SubRoutine I have the following code:
#IfDefShowStackStackDepth(1,48)'48 is a number given as an identifier of the routine. Each routine has a unique identifier, starting at 1, continuing to 52.#EndIf
Then at the end:
#IfDefShowStackStackDepth(0,48)#EndIf
This shows on the LCD display:
06>24
Which indicates that the Function GetLEeprom (ID.24) has been entered and the stack depth is 06.
01>01
Would suggest my StartUp routine (ID.01) has been entered and the stack depth is 01.
As the program progresses, I can now see in real time the stack depth, and which routine is currently being executed.
Now of course, it is fortunate that I have an LCD attached to my project to display the stack calls but if not it might be possible to store the two values in Eeprom whenever the highest value of stack depth is reached so that it is possible to later read from the processor's Eeprom the maximum stack depth reached, and the ID of the SubRoutine where that maximum was recorded.
This confirms to me that my current stack depth is around ten at it's maximum (I've added some since my guess of seven), not including the overhead for the GCB LED or LCD display routines used within my program.
Last edit: mkstevo 2017-10-13
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
If anyone has any better ideas, I'd like to hear them.
If I find myself twiddling my thumbs for a while, I might look at enhancing the AppleScript but at the moment I can't see how I can achieve what I'd like in AppleScript.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
mkstevo, "I am a big fan of the SubRoutine"
Yeah,they sort of new to me,not gosub but sub with values, getting there.
Is this a "for the sake of it project" as I can't see the point?
I thought the idea was things don't over flow and if they did it was human error.
My code has a mind of it's own :)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
No, I genuinely have a program with over 50 SubRoutines for my work. It suddenly crossed my mind while doing some quick work on a 16C57 that only has a two level stack, that I'd given no consideration to how many nested SubRoutine calls my program has. The majority of the SubRoutines do just fetch/return a value from Eeprom, or ensure that a value is within range, write a line of text to the LCD and so simply return immediately. Some however are quite deeply nested.
I have to be able to enter four different values which can be set to a value from 5 to 995 (but individual values must be at least greater than the preceeding one, and not greater than a certain limit for that value) then store these in Eeprom.
1.I ask if the values need updating,
2.check the switches and see if 'OK' is pressed, or exit adjustment,
3.fetch the first current value from Eeprom,
4.split the current value into Units Tens and Hundreds, (to speed up adjustment ,Units, Tens and Hundreds can be altered individually)
5.display the current value,
6.check the switches,
7.alter the Units value on the LCD if the Up/Down button is pressed,
8.move to adjust the Tens if the Left button pressed,
9.check the switches,
10.alter the Tens value,
11.check the switches,
12.if the 'OK' button pressed
13.recombine the value from the Units, Tens and Hundreds,
14.test to see if the value is within the range for the current adjustment, if not, return to step 3 without saving,
15.if within range store the value in Eeprom and move on to the next adjustment starting over from step 3 with a different value and range - unless all adjustments are complete and if so return to the main program loop...
Almost all of these are split into SubRoutines. As I say, a good percentage of these do their thing and then return to the calling SubRoutine, but some are indeed heavily and deeply nested. Not really wanting to start entirely from scratch, I thought it might be handy to try and find out what the heck is going on. I've printed the program out on paper, but it is difficult to see the program flow when it stretches to so many pages!
If I can avoid a programmatical error now before release, I'd quite like to. It does all work in testing, but there is that nagging doubt that, if it is a Tuesday, in July, the sun hasn't set, the button was pressed twice instead of once, an error will occur that will cause a major nuclear event and I'm going to be stood around looking at the floor shuffling my feet...
Borland Delphi which I used for probably fifteen years, was rather strict on program syntax, most program content had to be contained within what Delphi called Procedures (essentially SubRoutines) or Functions. All Procedures and Functions had to be declared in advance of them being used, as did all variables. Global variables were not considered 'good form', local variables were only local to the specific Procedure or Function they were declared within. Now this was good, it taught me not to rely on GoTo statements and to break all tasks down into reuseable code. I still tend to program in a similar way. If I see that I am doing something more than once, it goes into a SubRoutine. The problem is, that as programs develop from an initial idea and more functionality is requested from me then added by me, more and more items become reused and so more and more program goes into SubRoutines. Then I start to lose track of what is going on, again.
Last edit: mkstevo 2017-10-13
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
An idea. Look at SimulIDE by Santiago González. This is a great piece of software. Ask Santi if he enhance his tools or does he know a method in the toolchain he uses that we automatically do what you need. I think he may be able to help as his toolchain is very clever.
A thought.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Stack depth has not been a huge concern of mine when programming PICS in BASIC. As a general rule I try to write source code so that subroutines seldom need to call other subroutines. When they do, I try to limit that to two or three levels.
It is easy enough to keep track when writing the code. However if no attention is paid to stack depth when writing the code, then you can have just as big of a mess as when misusing GOTO.
8-bit PICs can have a stack depth from 2 to 31 levels. See the datasheet for the device.
Obviously a PIC with a 2 level deep stack cannot take good advantage of GCB and should only be used for miminal non-demanding applications. Any library that uses more than 1 stack level will probably cause problems. Some libraries will likely not work. I would not use one of these chips unless absolutely desperate or where it was specified by the system designer. ( Then I would question his/her sanity)
If you really want to manage or debug the stack, then I would suggest using an 18F PIC that supports a 31-Level "Software Accessible Hardware Stack". See the datasheet for PIC18F25K22 as an example. With this chip and similar others you can read the STKPTR register to see where the stack is. Again see the datasheet.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
William. I wonder why people use old low spec pics. I agree the 18f25k22 is a good pic to experiment with. I upgraded to it from 16f876.
Comparing pics on microchip site is messy,scrolling left/right you can't see the device numbers on other lines. I suppose I could print it.The prices when you look them up is a shock sometimes.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Of course you are right. I should have paid more attention to the flow of the program long before it was (almost) finished. Sadly, not giving it any consideration earlier is now giving me sleepless nights.
I don't want to modify the stack, simply ensure I haven't exceeded the limits of it.
The 16C57 is still used in many of our products (still current) and on occasions I am asked to modify the code. Some of this is written in PICC by Custom Computer Services. Presumably down to the limitations of these early PICs this does indeed produce a .tre file which shows the subroutine 'nest', it also warns if a program has exceeded the stack limit.
@Stan.
It is surprising how easy it is to get up to fifty+ SubRoutines! I hadn't fully realised how many I had until I counted them. I'm at the stage where I am about to review my code and see if it can be 'tidied up' which may yet increase (or reduce?) the Sub. count... As the program memory is above 90% full, I need to see if some economies can be made.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello.
I am a big fan of the SubRoutine and tend to make my program up using (lots of) small repeated sections of code placed into Subs or Functions.
I read in my datasheet for the 16F1829 that it has a 16 level stack. I have read earlier on the forum that some of the GCB LCD and Print routines can add a few levels of 'Stack space' to the program, but how can I ensure that my programming hasn't caused (or potentially could cause) a Stack overflow?
I also read, having searched the forum for 'Stack' that I can look in the .lst file and count the stack 'Push and Pop' numbers. I have looked in the .lst file and can't find any references to either Stack, Push or Pop.
I've just been trying to build a flowchart for my program, in an attempt to track down each 'Sub' call, from within each SubRoutine. So far, it looks as though my own stack depth is no greater than 7 so I think I should be under the 16 level limit.
Is there a way of finding the Stack level from the .lst file (or any of the other files GCB creates)?
Can anyone suggest a plugin for SynWrite that can show the SubRoutine nesting?
Or am I stuck to running through the code and counting the Sub calls manually?
Last edit: mkstevo 2017-10-03
A question that Hugh will have to answer in terms of stack management and he may be able provide insights to impact of exceeding the stack limit.
The IDE has no tools to trace subs, sorry.
GCB has no push pop it seems but could be done inline asm. Could pop change the sub stack return path? Not that I want to do that, just interested.
PIC Processors are Harvard Architecture.
That means that they have separate Address and Data Busses.
It also means that they have no Stack Pointe.
In its place they have a fixed stack, the size of which varies from device to device.
As a result would not be able to easily, if at all, read the address and place it in a user stack, even if you manually maintained two registers as a stack pointer.
That is the main reason why there were no C Compilers for PIC in the early decades.
It is also a testament to Hugh’s tenacity in tackling a compiler for PIC Processors in the first place and his Genius for having pulled it off.
Thanks Chris. I think I'm still using z80 and 6502. My dumbness was continued with picaxe that has push and pop which is not good teaching.
Lots not to assume in GCB.
"That is the main reason why there were no C Compilers for PIC in the early decades." ??
The basic compilers I used in the 80's were 2 pass. 1st labelled addresses,2nd actually defined the addresses. Beyond me.
Well, I've put some thought into this, and haven't really got terribly far, but here is what I have come up with so far.
I wrote an AppleScript that reads the GCB source code, it determines the name of the SubRoutines and Functions. It then distils each SubRoutine into only it's external calls. This is written to a text file.
As ever, actions speak louder than words, so here is the result of the script on a trivial program:
Now that is great, I can see the SubRoutine calls. What I'd ideally like is to take this a step further, and incorporate the nested sub calls. So I end up with something like this:
So I can now see that the call to CaptureData results in a maximum stack depth of three (1.CatureData > 2.OutputData > 3.Wipe_LineArray). In this program it isn't difficult to track, but in the program I'm wrestling with, it is rather more difficult as it contains forty+ SubRoutines.
My next thought was to borrow from a program I wrote in Borland Delphi some years ago that captured keyboard and mouse events. This became impossible to trace in the usual way, as each time an event fires, a new event is generated, and as the trace debugger displays - it fires an event... So, for that I wrote an external program that listened to messages from the main executable, and wrote these to a text file. Each SubRoutine had an entry message and at each exit point an exit message. The Debugging program could then time each SubRoutine, and show the routine calls.
The output of this looks something like this:
Thinking how I could achieve a similar thing I came up with the following SubRoutine:
At the start of each SubRoutine I have the following code:
Then at the end:
This shows on the LCD display:
06>24
Which indicates that the Function GetLEeprom (ID.24) has been entered and the stack depth is 06.
01>01
Would suggest my StartUp routine (ID.01) has been entered and the stack depth is 01.
As the program progresses, I can now see in real time the stack depth, and which routine is currently being executed.
Now of course, it is fortunate that I have an LCD attached to my project to display the stack calls but if not it might be possible to store the two values in Eeprom whenever the highest value of stack depth is reached so that it is possible to later read from the processor's Eeprom the maximum stack depth reached, and the ID of the SubRoutine where that maximum was recorded.
This confirms to me that my current stack depth is around ten at it's maximum (I've added some since my guess of seven), not including the overhead for the GCB LED or LCD display routines used within my program.
Last edit: mkstevo 2017-10-13
If anyone has any better ideas, I'd like to hear them.
If I find myself twiddling my thumbs for a while, I might look at enhancing the AppleScript but at the moment I can't see how I can achieve what I'd like in AppleScript.
mkstevo, "I am a big fan of the SubRoutine"
Yeah,they sort of new to me,not gosub but sub with values, getting there.
Is this a "for the sake of it project" as I can't see the point?
I thought the idea was things don't over flow and if they did it was human error.
My code has a mind of it's own :)
No, I genuinely have a program with over 50 SubRoutines for my work. It suddenly crossed my mind while doing some quick work on a 16C57 that only has a two level stack, that I'd given no consideration to how many nested SubRoutine calls my program has. The majority of the SubRoutines do just fetch/return a value from Eeprom, or ensure that a value is within range, write a line of text to the LCD and so simply return immediately. Some however are quite deeply nested.
I have to be able to enter four different values which can be set to a value from 5 to 995 (but individual values must be at least greater than the preceeding one, and not greater than a certain limit for that value) then store these in Eeprom.
1.I ask if the values need updating,
2.check the switches and see if 'OK' is pressed, or exit adjustment,
3.fetch the first current value from Eeprom,
4.split the current value into Units Tens and Hundreds, (to speed up adjustment ,Units, Tens and Hundreds can be altered individually)
5.display the current value,
6.check the switches,
7.alter the Units value on the LCD if the Up/Down button is pressed,
8.move to adjust the Tens if the Left button pressed,
9.check the switches,
10.alter the Tens value,
11.check the switches,
12.if the 'OK' button pressed
13.recombine the value from the Units, Tens and Hundreds,
14.test to see if the value is within the range for the current adjustment, if not, return to step 3 without saving,
15.if within range store the value in Eeprom and move on to the next adjustment starting over from step 3 with a different value and range - unless all adjustments are complete and if so return to the main program loop...
Almost all of these are split into SubRoutines. As I say, a good percentage of these do their thing and then return to the calling SubRoutine, but some are indeed heavily and deeply nested. Not really wanting to start entirely from scratch, I thought it might be handy to try and find out what the heck is going on. I've printed the program out on paper, but it is difficult to see the program flow when it stretches to so many pages!
If I can avoid a programmatical error now before release, I'd quite like to. It does all work in testing, but there is that nagging doubt that, if it is a Tuesday, in July, the sun hasn't set, the button was pressed twice instead of once, an error will occur that will cause a major nuclear event and I'm going to be stood around looking at the floor shuffling my feet...
Borland Delphi which I used for probably fifteen years, was rather strict on program syntax, most program content had to be contained within what Delphi called Procedures (essentially SubRoutines) or Functions. All Procedures and Functions had to be declared in advance of them being used, as did all variables. Global variables were not considered 'good form', local variables were only local to the specific Procedure or Function they were declared within. Now this was good, it taught me not to rely on GoTo statements and to break all tasks down into reuseable code. I still tend to program in a similar way. If I see that I am doing something more than once, it goes into a SubRoutine. The problem is, that as programs develop from an initial idea and more functionality is requested from me then added by me, more and more items become reused and so more and more program goes into SubRoutines. Then I start to lose track of what is going on, again.
Last edit: mkstevo 2017-10-13
This is interesting. I understand the challenge.
An idea. Look at SimulIDE by Santiago González. This is a great piece of software. Ask Santi if he enhance his tools or does he know a method in the toolchain he uses that we automatically do what you need. I think he may be able to help as his toolchain is very clever.
A thought.
How to get 50 subs. Not really.
goto sub1
retsub1:
end
sub1
goto sub2
retsub2: goto retsub1
sub2
goto sub3
retsub3: goto retsub2
sub3
code..47 more subs
goto retsub3
edit I was jesting.
You could stick all nested subs in line and save the call and returns.
Last edit: stan cartwright 2017-10-14
Stack depth has not been a huge concern of mine when programming PICS in BASIC. As a general rule I try to write source code so that subroutines seldom need to call other subroutines. When they do, I try to limit that to two or three levels.
It is easy enough to keep track when writing the code. However if no attention is paid to stack depth when writing the code, then you can have just as big of a mess as when misusing GOTO.
8-bit PICs can have a stack depth from 2 to 31 levels. See the datasheet for the device.
Obviously a PIC with a 2 level deep stack cannot take good advantage of GCB and should only be used for miminal non-demanding applications. Any library that uses more than 1 stack level will probably cause problems. Some libraries will likely not work. I would not use one of these chips unless absolutely desperate or where it was specified by the system designer. ( Then I would question his/her sanity)
If you really want to manage or debug the stack, then I would suggest using an 18F PIC that supports a 31-Level "Software Accessible Hardware Stack". See the datasheet for PIC18F25K22 as an example. With this chip and similar others you can read the STKPTR register to see where the stack is. Again see the datasheet.
William. I wonder why people use old low spec pics. I agree the 18f25k22 is a good pic to experiment with. I upgraded to it from 16f876.
Comparing pics on microchip site is messy,scrolling left/right you can't see the device numbers on other lines. I suppose I could print it.The prices when you look them up is a shock sometimes.
@William.
Of course you are right. I should have paid more attention to the flow of the program long before it was (almost) finished. Sadly, not giving it any consideration earlier is now giving me sleepless nights.
I don't want to modify the stack, simply ensure I haven't exceeded the limits of it.
The 16C57 is still used in many of our products (still current) and on occasions I am asked to modify the code. Some of this is written in PICC by Custom Computer Services. Presumably down to the limitations of these early PICs this does indeed produce a .tre file which shows the subroutine 'nest', it also warns if a program has exceeded the stack limit.
@Stan.
It is surprising how easy it is to get up to fifty+ SubRoutines! I hadn't fully realised how many I had until I counted them. I'm at the stage where I am about to review my code and see if it can be 'tidied up' which may yet increase (or reduce?) the Sub. count... As the program memory is above 90% full, I need to see if some economies can be made.