We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hello,
I would like to create a firmware and an application running on the same device.
The application needs to call some functions of the firmware. The application can be updated whereas the firmware is still the same.
Therefore, i want to have 2 projects: one for the firmware (C coded) and one for the application (C coded).
I have seen that it's possible to forbid idata, xdata and code area in order to prevent the application to overwrite the firmware and its variables, but I have no clues on how to give my firmware prototype to my application. Of course parameters and functions' addresses must map with the firmware mapping.
Does anyone have an idea how I can do this?
Thanks for your help! Damien.
I think the above is a reasonable approach, but you might make the source nicer by either creating inline functions that sets the struct parameters and makes the call, or by using #define.
Using a vector table requires a bit harder coupling between the two parts. You have to look into parameter passing, and it isn't easy to extend a function with an extra parameter and have a new application auto-adjust to an older firmware.
If you have a fixed entry in the struct with API version, you can manage to handle situations where the application support a newer or older API version than the firmware. A newer firmware will realize when an older application fails to pass an extra parameter. A newer application may set an extra parameter in the struct that gets used by a new firmware but ignored by older firmware.
Ok, I understand the concept, but I have no idea how I should do this... Will my application be able to use functions with normal parameters?
LCD_display(param1, param2);
with a macro like
typedef struct { long param1; // long in order to accept long param2; // all kind of parameters long param3; } struct_param; struct_param p; void firm_func(char func_type, struct_param *p); #define LCD_display(_x, _y) \ { \ p.param1 = _x; \ p.param2 = _y; \ p.param3 = 0; \ firm_func(0x01, &p); \ }
I don't really like the global "struct_param p" but I have no ideas how to remove it. And how do have I returned values?
result = my_function(param1, param2);
The previous macro does not allow returned values. How can I do this?
The firmware function will also generate lots of code, something like:
void firm_func(char func_type, struct_param *p) { switch(func_type) { case 0x01: LCD_display((char)p->param1, (char)p->param2); break; case 0x02: FlashRead((int)p->param1, (int)p->param2, (char*)p->param3); break; } }
generates around 25 bytes for a function with one parameter and 60 bytes for a function with 3 parameters. This is really too much... (81 functions of 1 parameter => 2025 bytes!!)
I need to find an other solution. Maybe it was not what you were thinking... Then give me some more hints please.
Damien.
First of all - the chip isn't the worlds most powerful. Hence the question if you should take this fight.
Second - if you really do make a "firmware" part, the firmware can set parameters in the struct before returning. Your inline function or define can then pick up this value.
Third - you can't afford a switch that extracts all parameters and immediately calls the individual functions. You might save most code space by performing a copy of the struct to a fixed location in the firmware RAM space, and have the individual functions pick up their parameters directly from that global struct. Or, if you can have the caller know the address of the struct and setting the parameters in a shared struct.
"This is really too much... (81 functions of 1 parameter => 2025 bytes!!)"
Writing all-purpose, general software and tight, compact code are (almost always) mutually exclusive!
In essence, that's why PCs have (and use!) huge resources, and 8051s don't.
So, as Per says, you need to decide what your real requirement is here - do you want something small, compact, and efficient, or something very general purpose?
If you want small, compact, and efficient, then an 8051 is probably ideal; if you don't, then it isn't.
the simple solution would be to abandon all ideas of a 'homemade' bootloader, just compile and link all of it in one piece.
Then use a chip (there are sooooooo many) with ISP. You can get funky Atmel ISP, serial port ISP, JTAG ISP, USB ISP and probably some other flavors.
The impression your posts leave is that you are trying to code the '51 as a PC, GIVE IT UP, many have tried and all have failed.
The '51 is absolutely wonderful when used as intended and absolutely horrible when not.
Thus either code the '51 as a '51 or use some other chip.
Erik
Well, this thread went a bit out of the topic.
I have to develop on a 73S1113 chip based on 8052, and I have to develop something that can download an application via USB (ISP is deactivated). This is not optional.
I wanted to know how to be sure that my firmware and my application can cohabit, knowing that I have "only" 64kb of flash and the application will use most of it.
I understood that there were different methods in order to do this work. The vector table seems interesting and I think I'll choose this method.
I understood that registers are automatically selected by the compiler (I don't need to worry about it) www.keil.com/.../c51_le_passingparmsinregs.htm
I just have to find how to tell the linker to not use some part of ibit and idata. So far, my attempts were not successful (using IBIT and IDATA linker directive).
Damien
It should be obvoous that if you change the bootloader/BIOS you need to change the app as well, the below requires that.
write an assembler module with all global BDATA/DATA/IDATA/PDATA/XDATA that the bootloader/BIOS uses and link it with both bootloader/BIOS and app. for the bootloader/BIOS write a .h module that defines all variables in the asm module as extern.
If you want to, you can even link that with the app.
There will be a requirement that all functions are declared reentrant to make them not use call tree since one will not be aware of the other. If main() can not be declared reentrant there can be no locval variables in main()