News:

Let's find out together what makes a PIC Tick!

Main Menu

Declaring a variable inside code

Started by trastikata, Oct 16, 2022, 08:48 AM

Previous topic - Next topic

trastikata

I was wondering if there's any difference for the pre-processor where exactly the variable is being declared - meaning if I declare it on-the-go inside the program code instead of at the beginning of the program?

TimB


I'm sure Les will correct me..

From memory the compiler runs many passes and builds up the list of variables before allocating the variable address's


shantanu@india

Are you trying dynamic variable creation like Python?
Regards
Shantanu

trastikata

Something like that.

Instead of declaring everything at the top of the program, I tried declaring (Dimension) new variables when I need them while writing the code and it seems to work just fine - this is the reason I am asking if there is any actual difference.

I am assuming that the pre-processor scans the entire code for Dim statements before compiling and creates a list of variables to be used and assigns memory space. Thus I was wondering if my assumption is correct?
 

shantanu@india

If it's working then it's OK... but what advantage are you actually getting?.. Readability?
Does variabled created inside procedures have limited life? In that case you can try that to free up resources.
Les can clarify.
Regards
Shantanu

John Drew

#5
Variables once created anywhere in a program occupy RAM and are never destroyed and the space is not available for re-use by another variable.
John

Edit:
By way of explanation a variable dimensioned inside a procedure has the name of the procedure appended to it by the compiler. It is actually possible to work with this outside the procedure. But why would you? It would destroy the clarity and ease of programming his solution gives us.

Les has been very clever in how he has developed the procedure mechanism to operate with a flat language and the limited stack size of the PIC. At the same time ensuring the speed of Positron is maintained. It's a masterstroke.
J

top204

RAM re-using was somethng on the cards before my injury, and I will be getting back to it once we settle back home. However, RAM re-usage also means a compiler's generated assembler code is not as efficeint, because it cannot designate RAM in specific areas, and the initial RAM usage is high, and sometimes, higher than a small device can handle, so it is not always as good as it seems.

On the PIC24 and dsPIC33 devices, this is not a problem, but on the 8-bit devices with their fragmented and banked RAM, it can cause dreadful assembler code when RAM from different areas is being used with all the RAM bank changes required!

The Positron8 and Positro16 compilers manipulate their RAM differently because of the fragmented and banked RAM on the 8-bit deivces, so in Positron8, a variable must be created in the code listing before it is used, but in Positron16, once it is created in any part of the program's listing, it should be visible everywhere.

I will be getting back into this with the 8-bit compiler when I get a chance because I have had some ideas floating around in the working part of my brain :-), that could also make it operate the same as the 16-bit compiler.

John Drew

Hi Les,
Looking at the pros and cons if you do develop re-usable variable space it may be better to restrict it to the 16bit chips.
In the 8 bit devices I just like fast and compact.
John

John Lawton

I like fast and compact too.

How often does it happen that there is insufficient RAM, I don't think that has ever happened to me?
Obviously large arrays can soak up RAM but there are ways to re-use single variables i.e. use them in more than one place by careful coding if things get tight.

John

shantanu@india

Cannot but agree with John and John.
The present compiler generates fast and compact code for RAM-starved 8-bit PICs. For the past 18 years I have faced RAM overflow only once in a 12F508A application which was promptly replaced later with a 12F675.Never again..
RAM overflow might occur if we allocate huge static arrays.. but why should we? There are other devices for that!!
Regards
Shantanu

trastikata

#10
Quote from: top204 on Oct 17, 2022, 08:31 AMso in Positron8, a variable must be created in the code listing before it is used

Thus the pre-processor in Positron8 scans for variables declarations and creates a list? Declaring a variable after being used in code works just fine. For example this code:

Device = 18F46J53
Declare Xtal = 4
Main:
    a = b + c
    b = a - c
    c = a - b
    Dim a As Byte
    Dim b As Byte
    Dim c As Byte
   
    GoSub AfterMain
    End
AfterMain:   
    d = a + b + c
    Dim d As Byte
Return 

John Drew

G'day Trastikata,
Look in the ASM file. You'll see all the variables declared at the top of the file ready for the assembler.
John

Stephen Moss

Quote from: John Drew on Oct 17, 2022, 09:51 AMLooking at the pros and cons if you do develop re-usable variable space it may be better to restrict it to the 16bit chips.
In the 8 bit devices I just like fast and compact.
I am not sure what the point of that would be, you cannot create more code space dynamically by adding and more importantly removing variables during code execution as the maximum code space is determined by the maximum number of variable used at any particular point in the program which all need space allocated.

If the point is to use a particular address for storing variable A in one part of the code and rename it for use with storing variable B in another part of the code because you are short of space then...
  • You need to be very, very careful with your code to ensure that you are not trashing the variable value you need to access later by overwriting it with the other variable value. Sounds like a risky ploy to me, get it wrong and you could be very confused with regard to the results that produces and what is causing it.
  • Can not the same thing effectively be achieved with Alising? i.e.
    Usage_1:     'Original Declaration for which memory space is allocated
    Dim Red_Dwarf as word   
    Red_Dwarf = ADin1

    [Some code here]

    Usage_2:     'Reusing Variable Red_Dwarf with a more relevant name in another part of the program
    Dim Lister as Red_Dwarf     
    Lister = $1234     'Red_Dwarf now contains $1234, not the ADC Result

    [Some more code here]

    Usage_3:     'Reusing Word Variable Red_Dwarf as 2 byte variables with a more relevant names in another part of the program
    Dim Rimmer as Red_Dwarf.LowByte
    Dim Cat as Red_Dwarf.HighByte

    Rimmer = PortB     'Red_Dwarf.LowByte now contains PortB value, not $34 or the ADC result low byte
    Cat = Rimmer - 4   'Red_Dwwarf.HighByte now Contain PortB value - 4, not $12 or the ADC result high byte
As at compiler time the alias names would just point to relevant variable address, or am I missing something?
Although perhaps using ReDim as the command instead of Dim my make it clearer that it is a variable re-use rather than a re-name.

John Drew

G'day Stephen,
There is a point to true local variables in procedures and functions but as Les pointed out it involves a lot of extra code which can defeat the purpose. I suspect most of us run out of code space well before we run out of RAM in PICs.

As an example of true local variables, in Delphi a variable declared in a procedure is created at entry, used within the procedure and destroyed on leaving the procedure with RAM being released for use elsewhere. So at any time RAM variables total global variables plus those declared in one procedure (or function). That's not true though if the procedure calls another procedure in which case it's global + Procedure 1 + Procedure 2 until procedure 2 exits.

The theory of true local variables is good for most processors but it falls down in 8 bit PICs because the mechanism to do it involves complexity that costs more than it saves. Apparently local variables are not quite so complex to implement in 16 bit devices. Hence my comment. In reality, I'm happy with the way Les has implemented pseudo local variables as a reasonable compromise. It's all I need and has many advantages over Gosub. Procedures are a giant leap forward. I apologise if you already know the above.
Cheers
John

TimB


There is another compiler "Firewing" that is very good, a very well written Pic compiler that handle stuff like Structures, Unions and User Types. Unfortunately it stopped being supported about 8 years back.

Structures, Unions and User Types are over my head but I did try it. I always found that simple programs eat the RAM, never enough to cause an issue as it never maxed out the device and there is no harm using 99% of ram as you get nothing for the unused ram in a device.

What I personally found was that there was nothing I could not do with Proton that I could with Firewing. In fact I coded a Pic18F14K50 that used ever ounce of the ram and rom that I'm very sure I could not do with any other compiler. Adding complexity  with features like Structures, Unions and User Types just uses ram and rom. Its ok on bigger devices where you have loads of both and want to speed up development with other compilers code snippets but for all my projects I like the simple, compact and fast code Proton produces.

I'm to lazy to get into learning new techniques. If I cannot code it in Proton on an 18f then its not for me. When People ask what I do apart from some of the other stuff I answer with I code pics, tiny devices you will get in a toaster. That is where Proton excels code small enough to fit in a tiny 10f device but powerful enough to drive a GLCD at speed and still have time and room to spare.

 

Stephen Moss

Quote from: John Drew on Oct 18, 2022, 12:37 PMAs an example of true local variables, in Delphi a variable declared in a procedure is created at entry, used within the procedure and destroyed on leaving the procedure with RAM being released for use elsewhere. So at any time RAM variables total global variables plus those declared in one procedure (or function). That's not true though if the procedure calls another procedure in which case it's global + Procedure 1 + Procedure 2 until procedure 2 exits.
Visual Basic does a similar thing with its Using command, but of course PIC's are not PC's which have a lot more RAM and are much faster.
I am not sure how they do it on PC's. I would think that to maximise the useable amount of RAM and minimise having to keep a record of both where the available spaces are and the size of those spaces so as to slot new variables in where they will fit that the most logical method would be to essentially defrag the RAM after freeing a resource.
Then you only need to retain a pointer to the bottom of the free RAM and the size of the free RAM to know if the new variable will fit, but defragging RAM in a PIC may not be so easy and slow.

I have come back to this as it occurred to me that one possible way of saving RAM not previously mentioned would be to use a mechanism similar to old fashioned page swapping back when PC's did not have much RAM.
In that you could store some of your variables in EEPROM, create a single variable big enough to hold the largest of those variables, i.e. Dim Reusable_RAM as DWORD. Then you simply move whatever variable you need for a specific part of the program from EEPROM into Reuseable_RAM when you need it, then when you have finished with it move it back to EEPROM so you can move another variable into Reusable_RAM when required.

I agree with you and Les that whatever method is used the code overhead to implement a system that dynamically creates and destroys variables on a PIC could use more resources than you would save, potentially swapping the problem of not enough RAM for that of not enough code space. 

JonW

Quote from: Stephen Moss on Oct 26, 2022, 08:54 AMI have come back to this as it occurred to me that one possible way of saving RAM not previously mentioned would be to use a mechanism similar to old fashioned page swapping back when PC's did not have much RAM.
In that you could store some of your variables in EEPROM, create a single variable big enough to hold the largest of those variables, i.e. Dim Reusable_RAM as DWORD. Then you simply move whatever variable you need for a specific part of the program from EEPROM into Reuseable_RAM when you need it, then when you have finished with it move it back to EEPROM so you can move another variable into Reusable_RAM when required.
 

I needed more Ram than the IC had for a large complex calibration procedure so I wrote the procedures to byte or page access the data flash memory.  I copy DFM segments to and from a much smaller section of Ram which includes arrays and general purpose registers, status, arrays, bit, byte, long, word, dword and float resisters (generic). Its slower but only limited with the flash.


John Drew

On the very few occasions I ran low on RAM I was able to save a few bytes by creating global variables such as bTemp1, bTemp2 or fTemp1 etc and then re-using these in all the subroutines knowing I had to always assign them a value before using them further in the routine.
Untidy perhaps, and sacrificing meaningful names, but saving half a dozen bytes was often enough. Good commenting avoided confusion.
John

John Lawton

Sometimes you have to <break the rules> to get a job done. A good engineer is pragmatic.

JohnB

You don't have to loose readability when re-using temporary variables in procedures, simple alias a meaningful name to the temporary variable at the beginning of the procedure.
JohnB