When trying to generate bindings for the bmx280 device driver, I noticed that the size of the struct bmx280_params_t differs from C to Rust. I used the module USEMODULE += bmp280_i2c and added these lines to rust-riot-sys/riot-headers.h to generate the bindings:
#include "bmx280.h"
#include "bmx280_params.h"
The struct contains an i2c_dev of type i2c_t which seems to be the reason for the difference. I used the following programs to print the sizes. In C:
#include <stdio.h>
#include <bmx280.h>
int main(void) {
printf("sizeof bmx280_params_t = %d\n", sizeof(bmx280_params_t));
printf("sizeof i2c_t = %d\n", sizeof(i2c_t));
}
sizeof bmx280_params_t = 32
sizeof i2c_t = 4
In Rust:
use riot_wrappers::{println, riot_main};
use riot_sys::{bmx280_params_t, i2c_t};
use core::mem::size_of;
riot_main!(main);
fn main() {
println!("sizeof bmx280_params_t = {}", size_of::<bmx280_params_t>());
println!("sizeof i2c_t = {}", size_of::<i2c_t>());
}
sizeof bmx280_params_t = 28
sizeof i2c_t = 1
In the file RIOT/drivers/include/periph/i2c.h the type is defined as follows:
#ifndef HAVE_I2C_T
typedef uint_fast8_t i2c_t;
#endif
For native, xtensa, risc-v and arm, it seems like all the fast integer types are defined through internal compiler defines like typedef __INT_FAST8_TYPE__ int_fast8_t; These types differ since RIOT is compiled by gcc and the Rust bindings are created with llvm. I did some tests and got the following types using different architectures. The cells show the actual type when using gcc / clang:
| Bit |
Native (x86-64) |
Xtensa |
RISC-V |
ARM |
| 8 |
char / char |
int / char |
int / char |
int / char |
| 16 |
long int / short |
int / short |
int / short |
int / short |
| 32 |
long int / int |
int / int |
int / int |
int / int |
| 64 |
long int / long int |
long long int / long long int |
long long int / long long int |
long long int / long long int |
One workaround is to redefine these macros when generating the bindings so that they match the types used by gcc. For the uint_fast8_t this can be done by adding the following lines to riot-bindgen.h and riot-c2rust.h:
#undef __UINT_FAST8_TYPE__
#define __UINT_FAST8_TYPE__ unsigned int
Alternatively, this also works:
#define HAVE_I2C_T
typedef unsigned int i2c_t
However there should be a better way that actually guarantees that the same type is used when compiling RIOT and creating the bindings.
When trying to generate bindings for the
bmx280device driver, I noticed that the size of the structbmx280_params_tdiffers from C to Rust. I used the moduleUSEMODULE += bmp280_i2cand added these lines torust-riot-sys/riot-headers.hto generate the bindings:The struct contains an
i2c_devof typei2c_twhich seems to be the reason for the difference. I used the following programs to print the sizes. In C:In Rust:
In the file
RIOT/drivers/include/periph/i2c.hthe type is defined as follows:For native, xtensa, risc-v and arm, it seems like all the fast integer types are defined through internal compiler defines like
typedef __INT_FAST8_TYPE__ int_fast8_t;These types differ since RIOT is compiled by gcc and the Rust bindings are created with llvm. I did some tests and got the following types using different architectures. The cells show the actual type when using gcc / clang:long long int
long long int
long long int
One workaround is to redefine these macros when generating the bindings so that they match the types used by gcc. For the
uint_fast8_tthis can be done by adding the following lines toriot-bindgen.handriot-c2rust.h:Alternatively, this also works:
However there should be a better way that actually guarantees that the same type is used when compiling RIOT and creating the bindings.