This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

variable

How to declare and assign variables in ROM to an specific address

Parents
  • Is the demand real? Is it really difficult to implement?

    Yep. The demand for this is obviously real. And, Yep, it's also going to be difficult to implement.

    It is on the list of feature that we will add, however, there are lots of features on this list and many of them have no work-arounds. Needless to say, we fix or implement those first.

    Since there are numerous ways to use fixed addresses for checksums, CRCs, configuration data, MCU reset configurations, and so on WITHOUT locating an initialized variable, it is not considered a critical situation.

    Let me elaborate...

    Checksum/CRC

    Does the C program REALLY need to assign the value of the CRC or checksum as shown in the following code?

    unsigned int code my_programs_crc = 0x1234 _at_ 0xFFFF;
    

    I don't even think so. The checksum or CRC is best done by an external utility that calculates it based on the contents of the HEX file or BINARY ROM image. The value can be stuffed into the HEX or BINARY file and when that's done the following works just fine.

    unsigned int code my_programs_crc _at_ 0xFFFF;
    

    The following knowledgebase article gives a little more information on using checksums.

    http://www.keil.com/support/docs/494.htm

    FYI, I've never used a variable in my program to hold the checksum or CRC. I always force the checksum or CRC to come out to a fixed value (like the binary representation of my initials). I calculate the CRC/checksum for the whole ROM and the result better be 0x4A57 (JW). I use the last 2 bytes of the ROM for my offset value. Then tell the linker that the rom is 0xFFFD bytes long (instead of 0x10000). Of course, I calculate the checksum over the whole ROM.


    Configuration Data

    Why should configuration data should be pre-initialized? Suppose you have configuration data defined as follows:

    struct config_st
      {
      unsigned int hi;
      unsigned int lo;
      unsigned int span;
      unsigned int hi_limit;
      unsigned int lo_limit;
      unsigned char warm_start_flag;
      };
    
    xdata struct config_st my_config_data =
      {
        20, 4, 16, 18, 7
      } _at_ 0x1000;
    

    The configuration data gets initialized EVERY TIME the program runs (i.e. on every reset). Is that REALLY what you want to happen? If there is a way to CHANGE the configuration data dynamically, wouldn't the following be better?

    struct config_st
      {
      unsigned int hi;
      unsigned int lo;
      unsigned int span;
      unsigned int hi_limit;
      unsigned int lo_limit;
      unsigned int warm_start_flag;
      };
    
    xdata struct config_st my_config_data _at_ 0x1000;
    
    code struct config_st reset_config_data = { 20, 4, 16, 18, 7, ~0xDEAD };
    
    .
    .
    .
    if (my_config_data.warm_start_flag != ~0xDEAD)
      {
      // Config Data is Trash -- Cold Start
      my_config_data = reset_config_data;
      }
    else
      {
      // Config Data is OK
      }
    


    MCU Configuration

    Sometimes it would be nice to initialize a specific memory address from C for microcontrollers that have memory-mapped (CODE) configuration registers. For all of these devices, we have implemented special startup code (because in many cases just setting some value in ROM is not all that is required).


    Conclusions?

    Ii is very rare to see an application that absolutely requires initialized data at a specific address. Typically, someone wants to be able to look at an address in the ROM to see what the configuration parameters are set to or something like that. In that case, my suggestion is to put the data in a struct and locate it using the linker.

    That's the one case where I agree that initializing and locating at the same time would be a convenient feature.

    Jon

Reply
  • Is the demand real? Is it really difficult to implement?

    Yep. The demand for this is obviously real. And, Yep, it's also going to be difficult to implement.

    It is on the list of feature that we will add, however, there are lots of features on this list and many of them have no work-arounds. Needless to say, we fix or implement those first.

    Since there are numerous ways to use fixed addresses for checksums, CRCs, configuration data, MCU reset configurations, and so on WITHOUT locating an initialized variable, it is not considered a critical situation.

    Let me elaborate...

    Checksum/CRC

    Does the C program REALLY need to assign the value of the CRC or checksum as shown in the following code?

    unsigned int code my_programs_crc = 0x1234 _at_ 0xFFFF;
    

    I don't even think so. The checksum or CRC is best done by an external utility that calculates it based on the contents of the HEX file or BINARY ROM image. The value can be stuffed into the HEX or BINARY file and when that's done the following works just fine.

    unsigned int code my_programs_crc _at_ 0xFFFF;
    

    The following knowledgebase article gives a little more information on using checksums.

    http://www.keil.com/support/docs/494.htm

    FYI, I've never used a variable in my program to hold the checksum or CRC. I always force the checksum or CRC to come out to a fixed value (like the binary representation of my initials). I calculate the CRC/checksum for the whole ROM and the result better be 0x4A57 (JW). I use the last 2 bytes of the ROM for my offset value. Then tell the linker that the rom is 0xFFFD bytes long (instead of 0x10000). Of course, I calculate the checksum over the whole ROM.


    Configuration Data

    Why should configuration data should be pre-initialized? Suppose you have configuration data defined as follows:

    struct config_st
      {
      unsigned int hi;
      unsigned int lo;
      unsigned int span;
      unsigned int hi_limit;
      unsigned int lo_limit;
      unsigned char warm_start_flag;
      };
    
    xdata struct config_st my_config_data =
      {
        20, 4, 16, 18, 7
      } _at_ 0x1000;
    

    The configuration data gets initialized EVERY TIME the program runs (i.e. on every reset). Is that REALLY what you want to happen? If there is a way to CHANGE the configuration data dynamically, wouldn't the following be better?

    struct config_st
      {
      unsigned int hi;
      unsigned int lo;
      unsigned int span;
      unsigned int hi_limit;
      unsigned int lo_limit;
      unsigned int warm_start_flag;
      };
    
    xdata struct config_st my_config_data _at_ 0x1000;
    
    code struct config_st reset_config_data = { 20, 4, 16, 18, 7, ~0xDEAD };
    
    .
    .
    .
    if (my_config_data.warm_start_flag != ~0xDEAD)
      {
      // Config Data is Trash -- Cold Start
      my_config_data = reset_config_data;
      }
    else
      {
      // Config Data is OK
      }
    


    MCU Configuration

    Sometimes it would be nice to initialize a specific memory address from C for microcontrollers that have memory-mapped (CODE) configuration registers. For all of these devices, we have implemented special startup code (because in many cases just setting some value in ROM is not all that is required).


    Conclusions?

    Ii is very rare to see an application that absolutely requires initialized data at a specific address. Typically, someone wants to be able to look at an address in the ROM to see what the configuration parameters are set to or something like that. In that case, my suggestion is to put the data in a struct and locate it using the linker.

    That's the one case where I agree that initializing and locating at the same time would be a convenient feature.

    Jon

Children
  • Jon,
    You should be considering how best the problem can be solved instead of trying to catalog all the possible situations in which it occurs. My issue is that my system stores code in flash memory, and I reserve a sector for configuration storage there, too. I'd like to burn the default configuration into flash before pc assembly. That saves an operation in manufacturing.

    My solution is not elegant, but it DOES work. My project includes an assembly language STARTUP file instead of letting the compiler generate start up. Within that file, I am free to locate and initialize a section of code memory. The name can be declared PUBLIC. For example,

    CSEG AT 0xc000
    PUBLIC config
    config:
    DB 0, 5, 0x55,...

    Over on the C side, I just declare

    extern code data_type config;

    No need to redeclare the address. The linker already knows its location.

    There are two obvious problems with this solution.
    * First, you MUST include an assembly language file in the build. Most of us would like to avoid that because it is non-portable.
    * Second, it's a maintenance nightmare. If the data struct is very complicated, it is SO easy to get off by one byte. Then it could take forever to get back on track.

    No doubt, it would be sweeter to write the whole thing in C, but depending on your desperation factor, you might stoop to my level.

    Jeff.

  • Jeff, my comments are not necessarily directed to you specifically. You just drew them out of me.

    Reordering quotations...

    "No doubt, it would be sweeter to write the whole thing in C, but depending on your desperation factor, you might stoop to my level."

    Well Jeff, I would contend that your level is just fine.

    "There are two obvious problems with this solution.
    * First, you MUST include an assembly language file in the build. Most of us would like to avoid that because it is non-portable."


    Maybe most of you would like to avoid assembly language because you don't know/like/whatever assembly language, but what is the "portable" way to do it? Certainly using an enhancement to a non-portable C language extension does not suddenly make it portable. If anything, the way you are doing it would be more familiar to those who have solved the same problem on other platforms, since it is the "conventional" way of going about it.

    "* Second, it's a maintenance nightmare. If the data struct is very complicated, it is SO easy to get off by one byte. Then it could take forever to get back on track."

    [rant]
    If a configuration data structure is complex enough to be even remotely considered a maintenance nightmare, you should be using other tools to generate it anyway. Folks should take their blinders off and expand their software tools use beyond just the assembler, compiler, linker. Software tools that were created by software developers for software developers, have been around a long time. In these cases of complex configuration data structures (especially if they change from time to time), consider expressing the configuration in form easy and familiar to the configurer, and use Perl, awk, etc. to programmatically generate an assembly source file from the configuration file as one step in your build process.
    [/rant]

    Sigh. I'm going to go off and reread Jon Bentley's "Programming Pearls" and "More Programming Pearls" for my mental health ;-)

  • Sorry, I just have to share this because it is so relevant.

    Just to illustrate the "maintenance nightmare" issue using an example, and this is literally an issue I was asked to help with today coincidentally...

    A client of mine who will remain nameless, is a "device" manufacturer. Their high-end parts are big and complex, having embedded CPU(s) and BGA packages with pin (ball) counts as high as 1704. The databook for this device is around 2000 pages. The databook contains pinout tables for 256, 456, 672, 676, 896, 1148, 1152, 1517, 1696, and 1704 pins (some pins missing in an otherwise regular "row" by "column" pin array). Some poor documentation fellow has to take the "source" package/pinout text documents output early in the IC design and packaging engineering process and verify BY HAND/EYEBALL that the PDF databook pinout tables match. For the big packages, these tables are upwards of 48 pages! Well guess what? A pin got dropped somewhere along the way in the databook creation process and a customer caught it. Oops. There you go. Now that's and error-prone maintenance nightmare! The documentation fellow has a remarkable attention to detail and ability to focus, but he's only human after all. He also gets to recheck all the other tables to verify that nothing has changed if there's an edit performed to another table.

    So what we do is use those marvelous text processing software tools my forefathers created for me, write a surprisingly simple script (program) to parse out the pins from the the engineering source document and the PDF (or from old PDF and new PDF), process one against the other looking for inconsistencies, and BAM! that poor fellow's hair starts growing back in.

    The moral of the story is that there is usually a way to make a complex problem simpler using the right tools. Since you are not likely to be the first one to encounter your complex problem or something very similar to it, there's a good chance that a tool exists to help you.