Compiler bug in passing var addresses before calling procedure ?
Posted: 02 Jan 2023 20:22
First : Happy year++
Hi,
we are in a rush to deliver a product based on PIC18F66K80. My compiler rev is 7.6.0.
Sometimes some floating point calculation results are corrupted. Powering OFF/ON the target sometimes fix the pb, sometimes not...
Sometimes, adding some dummy instructions somewhere in the beginning of the code has temporarily fixed the pb but adding code fails again !
It looks like a typical RAM corruption problem (string not 0 terminated, buffer overflow, ...).
That's why I started digging into the assembly listing.
On the board there is a temperature and pressure sensor (Bosch BMP388). To get the real compensated pressure and temperature, you have to do some maths using floating point operations. The pb we have is a corrupted pressure, but returned temperature is always OK.
The procedure used to calculate temperature and pressure is the following one (for sake of clarity only parts of code are shown here) ;
Which is compiled into :
This is a typical case of var passed by address. FSR1 is used here for indirect addressing to transfer the result of the division (R0 to R3) into the variable passed.
One can see that addresses $083D/$083E and $083F/$0840 are allocated by the compiler to store the address of the passed var :
A call of this procedure with two global real var gives that :
With this global var allocation :
As expected $0696 (address of gDumReal2) is stored into $083D/$083E (where procedure BMP_GetData is expecting the address of the temperature passed var).
Same for $068E stored into $083F/$0840.
But I do not understand why the compiler is passing the address of each var with 4 bytes (FARG_BMP_GetData_xxxx to FARG_BMP_GetData_xxxx+3)... 16 bit address should be enough for pointing a 3648 byte RAM.
This is erasing with 00s the 2 next RAM addesses, which is not a problem for gDumReal2, because it erases gDumreal1, but for dDumReal1, it is erasing (corrupting) another global var of the program...
Elsewhere in the code, the same procedure is called with a slight difference between the 2 passed var :
- gDumReal2 is a global var (in a unit called "Variables", used by main program)
- ch1press is a local var of the "CalibrationSequence()" procedure implemented in another unit used and called by the main program :
Which is compiled into :
Surprinsingly, address of ch1press is well passed with 16 bit before calling...
Conclusion: it seems that global and local var are not passed in the same way. And global var address is not passed as it should do.
This is certainly not the root cause of my problems, but to my (tired....) eyes it is a big potential cause of var corruption.
Thanks for your feedback !
Hi,
we are in a rush to deliver a product based on PIC18F66K80. My compiler rev is 7.6.0.
Sometimes some floating point calculation results are corrupted. Powering OFF/ON the target sometimes fix the pb, sometimes not...
Sometimes, adding some dummy instructions somewhere in the beginning of the code has temporarily fixed the pb but adding code fails again !
It looks like a typical RAM corruption problem (string not 0 terminated, buffer overflow, ...).
That's why I started digging into the assembly listing.
On the board there is a temperature and pressure sensor (Bosch BMP388). To get the real compensated pressure and temperature, you have to do some maths using floating point operations. The pb we have is a corrupted pressure, but returned temperature is always OK.
The procedure used to calculate temperature and pressure is the following one (for sake of clarity only parts of code are shown here) ;
Code: Select all
procedure BMP_GetData(var temperature, pressure:real);
var p_lin:real;
begin
...
pressure := p_lin/100.0; // p_lin is a partial data, local var
end;
Code: Select all
;BMP388.mpas,442 :: pressure := p_lin/100.0;
0x26B0 0x0E00 MOVLW 0
0x26B2 0x6E04 MOVWF R4
0x26B4 0x0E00 MOVLW 0
0x26B6 0x6E05 MOVWF R5
0x26B8 0x0E48 MOVLW 72
0x26BA 0x6E06 MOVWF R6
0x26BC 0x0E85 MOVLW 133
0x26BE 0x6E07 MOVWF R7
0x26C0 0xF008ECE8 CALL _Div_32x32_FP, 0
0x26C4 0xFFE1C83F MOVFF FARG_BMP_GetData_pressure, FSR1L
0x26C8 0xFFE2C840 MOVFF FARG_BMP_GetData_pressure+1, FSR1H
0x26CC 0xFFE6C000 MOVFF R0, POSTINC1
0x26D0 0xFFE6C001 MOVFF R1, POSTINC1
0x26D4 0xFFE6C002 MOVFF R2, POSTINC1
0x26D8 0xFFE6C003 MOVFF R3, POSTINC1
;BMP388.mpas,460 :: end;
L_end_BMP_GetData:
0x26DC 0x0012 RETURN 0
; end of _BMP_GetData
One can see that addresses $083D/$083E and $083F/$0840 are allocated by the compiler to store the address of the passed var :
Code: Select all
0x083D [2] FARG_OLED_DrawPixel_pos_x
0x083D [2] FARG_BMP_GetData_temperature <------ address allocated (2 bytes) to pass the address of passed TEMPERATURE
0x083D [1] FARG_OLED_SendData_cmd
0x083D [4] TMC_Home_dummyDat
0x083D [4] FARG_TMC_GoToPosition_position
0x083D [1] FARG_OLED_SendCommand_cmd
0x083D [4] FARG_floor_x
0x083D [1] FARG_UART_Write_data_
0x083D [2] FARG_LEDRGB_FillChannel2Buffer_color
0x083D [4] FARG_LongIntToByte_value
0x083D [1] LEDRGB_Show_i
0x083D [2] FARG_LEDRGB_FillChannel1Buffer_color
0x083F [1] LEDRGB_FillChannel2Buffer_i
0x083F [1] LEDRGB_FillChannel1Buffer_i
0x083F [2] FARG_BMP_GetData_pressure <----- address allocated (2 bytes) to pass the address of passed PRESSURE
0x083F [2] FARG_OLED_DrawPixel_pos_y
0x0841 [6] BMP_GetData_measurements
0x0841 [4] floor_local_result
0x0841 [1] FARG_OLED_DrawPixel_color
0x0841 [2] FARG_LongIntToByte_byteArray
0x0845 [2] floor_expon
Code: Select all
;Init.mpas,104 :: BMP_GetData(gDumReal2,gDumReal1);
0x520C 0x0E96 MOVLW _gDumReal2
0x520E 0x0108 MOVLB 8
0x5210 0x6F3D MOVWF FARG_BMP_GetData_temperature, 1
0x5212 0x0E06 MOVLW hi_addr(_gDumReal2)
0x5214 0x6F3E MOVWF FARG_BMP_GetData_temperature+1, 1
0x5216 0x0E00 MOVLW higher_addr(_gDumReal2)
0x5218 0x6F3F MOVWF FARG_BMP_GetData_temperature+2, 1
0x521A 0x6F40 MOVWF FARG_BMP_GetData_temperature+3, 1
0x521C 0x0E8E MOVLW _gDumReal1
0x521E 0x6F3F MOVWF FARG_BMP_GetData_pressure, 1
0x5220 0x0E06 MOVLW hi_addr(_gDumReal1)
0x5222 0x6F40 MOVWF FARG_BMP_GetData_pressure+1, 1
0x5224 0x0E00 MOVLW higher_addr(_gDumReal1)
0x5226 0x6F41 MOVWF FARG_BMP_GetData_pressure+2, 1
0x5228 0x6F42 MOVWF FARG_BMP_GetData_pressure+3, 1
0x522A 0xF010EC4D CALL _BMP_GetData, 0
Code: Select all
0x068E [4] _gDumReal1
0x0692 [4] _gAmbiantPressure
0x0696 [4] _gDumReal2
Same for $068E stored into $083F/$0840.
But I do not understand why the compiler is passing the address of each var with 4 bytes (FARG_BMP_GetData_xxxx to FARG_BMP_GetData_xxxx+3)... 16 bit address should be enough for pointing a 3648 byte RAM.
This is erasing with 00s the 2 next RAM addesses, which is not a problem for gDumReal2, because it erases gDumreal1, but for dDumReal1, it is erasing (corrupting) another global var of the program...
Elsewhere in the code, the same procedure is called with a slight difference between the 2 passed var :
- gDumReal2 is a global var (in a unit called "Variables", used by main program)
- ch1press is a local var of the "CalibrationSequence()" procedure implemented in another unit used and called by the main program :
Code: Select all
procedure CalibrationSequence();
var ch1press,ch2press: real;
calibOK, run, previousCalibOK : byte;
begin
...
BMP_GetData(gDumReal2,ch1press);
...
end:
Which is compiled into :
Code: Select all
;Init.mpas,63 :: BMP_GetData(gDumReal2,ch1press);
0x5134 0x0E96 MOVLW _gDumReal2
0x5136 0x0108 MOVLB 8
0x5138 0x6F3D MOVWF FARG_BMP_GetData_temperature, 1
0x513A 0x0E06 MOVLW hi_addr(_gDumReal2)
0x513C 0x6F3E MOVWF FARG_BMP_GetData_temperature+1, 1
0x513E 0x0E00 MOVLW higher_addr(_gDumReal2)
0x5140 0x6F3F MOVWF FARG_BMP_GetData_temperature+2, 1
0x5142 0x6F40 MOVWF FARG_BMP_GetData_temperature+3, 1
0x5144 0x0EF2 MOVLW CalibrationSequence_ch1press
0x5146 0x6F3F MOVWF FARG_BMP_GetData_pressure, 1
0x5148 0x0E07 MOVLW hi_addr(CalibrationSequence_ch1press)
0x514A 0x6F40 MOVWF FARG_BMP_GetData_pressure+1, 1
0x514C 0xF010EC4D CALL _BMP_GetData, 0
Conclusion: it seems that global and local var are not passed in the same way. And global var address is not passed as it should do.
This is certainly not the root cause of my problems, but to my (tired....) eyes it is a big potential cause of var corruption.
Thanks for your feedback !