|
|
View previous topic :: View next topic |
Author |
Message |
allenhuffman
Joined: 17 Jun 2019 Posts: 552 Location: Des Moines, Iowa, USA
|
const and extern |
Posted: Fri Feb 26, 2021 1:39 pm |
|
|
I recently ran in to a bug where a memcpy() was doing things in the wrong order, such as:
Code: |
memcpy (&defaultData, myData, sizeof(myData));
|
It looks like the code's intent was to copy default values over into a working structure, but the destination/source was reversed so it was overwriting the defaults.
Using the const keyword can make compilers catch such accidents, but I get a linker error when trying to extern the const in a header file, such as:
Code: |
HEADER FILE:
typedef struct
{
int a,b,c;
} MyStruct;
...
extern const MyStruct defaultData;
SOURCE FILE:
const MyStruct defaultData=
{
1, 2, 3
};
|
But I get a linker error on the CCS compiler, in my case complaining about "const1464" (did not appear anywhere in the map/list file). I feel like I've been down this path before, since I found I already had a C test program called ExternConst where I was doing this on a PC using GCC. _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19506
|
|
Posted: Sat Feb 27, 2021 2:31 am |
|
|
Thing is that const in CCS has a fundamentally different meaning to
const in GCC.
Const in ANSI C implies a standard variable that is designed so it should
not allow writes to it. So defining as const still has you using what is a RAM
based variable.
Const in CCS implies a 'variable' stored in the program memory, which in the
PIC is a different memory space to the RAM.
I'd have expected what you want to work, provided you select ANSI mode, or
CONST=READ_ONLY which should switch const to behaving like the ANSI
form. |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1346
|
|
Posted: Sat Feb 27, 2021 5:39 am |
|
|
And if you need "const" for actual constant data somewhere else, you can alternatively use the _readonly keyword as a replacement for the const you are normally used to. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Sat Feb 27, 2021 10:56 am |
|
|
I've always wondered how much 'overhead' is involved in making any variable like RAM or EEPROM 'read only' aka 'constant' ? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19506
|
|
Posted: Sun Feb 28, 2021 1:49 am |
|
|
I think it depends enormously on what chip is involved.
On most compilers, the 'const' declaration only affects the compile, not
the running code. It simply sets a flag so that if at any point in the code,
a call is made that potentially writes to the 'const', it raises an error for this.
Most don't/can't go further than this.
You can actually do things like pointer accesses and change a const,
on a lot of compilers.
Now a few chips do have hardware that can physically protect areas of
memory, and in some cases the const declaration instead uses this. This
does impose in some cases a lot of overhead.
Now the different CCS usage, is not a case of CCS being awkward. They used
const for the ROM, long before ANSI C used it. It is perhaps a pity that
the ANSI committee didn't elect to use a different keyword, like 'protected'
(which would better describe what const involves in this language), or
'unwritable'.
In the CCS 'const' use, the overhead depends on the chip version. On
the PIC16's, the program memory, only supports 14bit, so storing values
as const, adds extra maths to calculate where you are actually talking to,
and performing either RETLW instructions (which is a very efficient way
of retrieving simple values on these - on the early chips, the only way
of doing this), or the program memory read operation (which involves
several instructions more than a simple RAM read). On the PIC18's things
get easier. Unfortunately things get harder again on the PIC24/30/33,
though using PSV gets rid of this overhead (though there is a timing cost),
but restricts how much data can be stored, and uses more ROM space. |
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 552 Location: Des Moines, Iowa, USA
|
|
Posted: Tue Mar 02, 2021 10:11 am |
|
|
Ttelmah wrote: |
On most compilers, the 'const' declaration only affects the compile, not
the running code. It simply sets a flag so that if at any point in the code,
a call is made that potentially writes to the 'const', it raises an error for this. |
This is exactly what I wanted this for. To catch an attempt to memcpy to data that was supposed to be the source. _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ? |
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 552 Location: Des Moines, Iowa, USA
|
|
Posted: Fri Apr 30, 2021 9:19 am |
|
|
temtronic wrote: | I've always wondered how much 'overhead' is involved in making any variable like RAM or EEPROM 'read only' aka 'constant' ? |
I am curious too. Arduino has the "PROGMEM" stuff which puts variables in program space, and generates special code to access them (I dove in to these on a blog post one - https://subethasoftware.com/2014/02/20/arduino-memory-and-optimizations/).
Ideally I'd like constant data to be in program space, but it looks like I can't pass them in to functions (?) without the trick that copies them into RAM. Is there some kind of ROM Ptr equivalent in this CCS PIC world?
In my case, we have a runtime structure like this:
Code: |
typedef struct
{
int Setting1;
int Setting2;
} RuntimeStruct;
|
And we have an image in memory declared like this:
Code: |
RuntimeStruct g_Runtime =
{
0,
0
};
|
But if that needs to be reset to defaults, we also have a default declaration like:
Code: |
RuntimeStruct g_RuntimeDefaults =
{
42,
1955
};
|
When that needs to be reset, there is a memcpy (&g_Runtime, &g_RuntimeDefaults, sizeof(g_Runtime));
I was looking for a simple way to make g_RuntimeDefaults const so it could not be changed, since the original code bug I found had those src/dst parameters reversed and it was wiping out the default values instead of restoring to defaults. _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Apr 30, 2021 10:51 am |
|
|
allenhuffman wrote: | Is there some kind of ROM Ptr equivalent in this CCS PIC world? |
It's in the CCS manual:
Quote: |
Type Qualifiers (page 44):
rom - Forces data into program memory. Pointers may be
used to this data but they can not be mixed with RAM pointers.
[PCD] roml - Same as rom except only the even program memory locations are used.
|
and:
Quote: |
Using Program Memory for Data (page 48):
A special method allows the use of pointers to ROM. This method
does not contain extra code at the start of the structure, as does const.
For example:
char rom commands[] = {“put|get|status|shutdown”};
[PCD] ROML may be used instead of ROM if you only to use even memory locations.
|
CCS manual:
https://www.ccsinfo.com/downloads/ccs_c_manual.pdf |
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 552 Location: Des Moines, Iowa, USA
|
|
Posted: Fri Apr 30, 2021 12:40 pm |
|
|
Thanks -- I did quite a bit of experimenting with those last year. In case anyone else pops in to this, here's a test program I wrote testing rom versus const and such. It showed how to make a RAM variable (structure) that could contain pointer to constant data, and pass that around, and read that constant data in the function.
Is there a better approach?
Code: | #include <main.h>
#include <stdint.h>
rom BYTE TABLE1[10] = {1,2,3,4,5,6,7,8,9,10};
rom BYTE TABLE2[10] = {11,12,13,14,15,16,17,18,19,20};
rom BYTE TABLE3[10] = {21,22,23,24,25,26,27,28,29,30};
const uint8_t data1[] = { 1,2,3,4,5,6,7,8,9,10 };
const uint8_t data2[] = { 11,12,13,14,15,16,17,18,19,20 };
typedef struct
{
int x,y;
const uint8_t *ptr;
} MyStruct;
MyStruct one = { 1, 2, data1 };
MyStruct two = { 3, 4, data2 };
void show(MyStruct temp)
{
printf ("%d, %d - ", temp.x, temp.y);
for (int i=0; i<10; i++)
{
unsigned int value = temp.ptr[i];
printf ("%u ", value);
}
printf ("\r\n");
}
const int Test[] = { 1, 2, 3, 4, 5 };
void main()
{
printf (__DATE__" "__TIME__"\r\n");
printf ("&data1 = 0x%x\r\n", &data1);
printf ("&data2 = 0x%x\r\n", &data2);
printf ("one.ptr = 0x%x\r\n", one.ptr);
printf ("two.ptr = 0x%x\r\n", two.ptr);
show(one);
show(two);
printf ("sizeof(Test) = %u\r\n", sizeof(Test));
while(TRUE);
// SECOND TEST - edit as needed.
printf ("&TABLE1 = 0x%x\r\n", &TABLE1);
printf ("&TABLE2 = 0x%x\r\n", &TABLE2);
printf ("&TABLE3 = 0x%x\r\n", &TABLE3);
rom BYTE *Ptr = &TABLE2;
printf ("Ptr = 0x%x\r\n", Ptr);
for (int idx=0; idx<10; idx++)
{
unsigned int value = Ptr[idx];
printf("%d. %u\r\n", idx, value);
}
while(TRUE); // CCS compiler #define
}
// End of main.c |
_________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ? |
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 552 Location: Des Moines, Iowa, USA
|
|
Posted: Fri Apr 30, 2021 12:45 pm |
|
|
And for future reference -- this was a test program showing how to pass a ROM pointer in to a function:
Code: | #include <main.h>
#include <stddef.h> // NULL
#if 1
// Store this in ROM.
const int8 data1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
const int8 data2[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
#else
extern const int8 data1[];
extern const int8 data2[];
#endif
void function(rom int8 *ptr, int size)
{
printf ("function( 0x%x, %d)\r\n", ptr, size);
if (ptr != NULL)
{
for (int idx=0; idx < size; idx++)
{
// Load value from ROM.
int8 value = ptr[idx];
printf ("%d ", value);
}
printf ("\r\n");
}
}
void main()
{
printf (__DATE__" "__TIME__"\r\n");
// Show direct data.
printf ("data1 at 0x%x:\r\n", data1);
for (int idx=0; idx < sizeof(data1); idx++)
{
printf ("%d ", data1[idx]);
}
printf ("\r\n");
printf ("data2 at 0x%x:\r\n", data2);
for (int idx=0; idx < sizeof(data2); idx++)
{
printf ("%d ", data2[idx]);
}
printf ("\r\n");
// Call function.
function (data1, 10);
function (data2, 10);
while(TRUE) // CCS compiler #define
{
//TODO: User Code
}
}
// End of main.c
|
_________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ? |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|