1010
1111#include <stdint.h>
1212#include <csp/csp_types.h>
13- #include <vmem/vmem.h>
1413
1514#include <libparam.h>
1615
@@ -42,6 +41,31 @@ typedef enum {
4241 PARAM_TYPE_INVALID ,
4342} param_type_e ;
4443
44+ #define PARAM_CTYPE_PARAM_TYPE_UINT8 uint8_t
45+ #define PARAM_CTYPE_PARAM_TYPE_UINT16 uint16_t
46+ #define PARAM_CTYPE_PARAM_TYPE_UINT32 uint32_t
47+ #define PARAM_CTYPE_PARAM_TYPE_UINT64 uint64_t
48+ #define PARAM_CTYPE_PARAM_TYPE_INT8 int8_t
49+ #define PARAM_CTYPE_PARAM_TYPE_INT16 int16_t
50+ #define PARAM_CTYPE_PARAM_TYPE_INT32 int32_t
51+ #define PARAM_CTYPE_PARAM_TYPE_INT64 int64_t
52+ #define PARAM_CTYPE_PARAM_TYPE_XINT8 uint8_t
53+ #define PARAM_CTYPE_PARAM_TYPE_XINT16 uint16_t
54+ #define PARAM_CTYPE_PARAM_TYPE_XINT32 uint32_t
55+ #define PARAM_CTYPE_PARAM_TYPE_XINT64 uint64_t
56+ #define PARAM_CTYPE_PARAM_TYPE_FLOAT float
57+ #define PARAM_CTYPE_PARAM_TYPE_DOUBLE double
58+ #define PARAM_CTYPE_PARAM_TYPE_STRING char
59+ #define PARAM_CTYPE_PARAM_TYPE_DATA char
60+
61+
62+ /* “Selector” macro */
63+ #define PARAM_CTYPE (_ptype_token ) PARAM_CTYPE_##_ptype_token
64+
65+ /* Deriver sizeof/alignof fra token */
66+ #define PARAM_SIZEOF (_ptype_token ) (sizeof(PARAM_CTYPE(_ptype_token)))
67+ #define PARAM_ALIGNOF (_ptype_token ) (_Alignof(PARAM_CTYPE(_ptype_token)))
68+
4569/**
4670 * Global parameter mask
4771 */
@@ -90,37 +114,36 @@ typedef enum {
90114/**
91115 * Parameter description structure
92116 * Note: this is not packed in order to maximise run-time efficiency
117+ * But the order of the elements is chosen to minimize padding.
118+ * So we start by largest types first, down to smallest types.
93119 */
94120typedef struct param_s {
121+
122+ uint64_t vaddr ; /* Virtual address in case of VMEM */
95123
96- /* Parameter declaration */
97- uint16_t id ;
98124 uint16_t * node ;
99- param_type_e type ;
100- uint32_t mask ;
101125 char * name ;
102126 char * unit ;
103127 char * docstr ;
104-
105- /* Storage */
106128 void * addr ; /* Physical address */
107- uint64_t vaddr ; /* Virtual address in case of VMEM */
108- struct vmem_s * vmem ;
109- int array_size ;
110- int array_step ;
129+ const struct vmem_s * vmem ;
130+ void (* callback )(const struct param_s * param , int offset );
111131
112- /* Local info */
113- void (* callback )(struct param_s * param , int offset );
114- #ifdef PARAM_HAVE_TIMESTAMP
132+ #ifdef PARAM_HAVE_TIMESTAMP
115133 csp_timestamp_t * timestamp ;
116- #endif
117-
118- #ifdef PARAM_HAVE_SYS_QUEUE
134+ #endif
135+
136+ #ifdef PARAM_HAVE_SYS_QUEUE
119137 /* single linked list:
120- * The weird definition format comes from sys/queue.h SLINST_ENTRY() macro */
138+ * The weird definition format comes from sys/queue.h SLINST_ENTRY() macro */
121139 struct { struct param_s * sle_next ; } next ;
122- #endif
140+ #endif
123141
142+ uint32_t mask ;
143+ uint16_t id ;
144+ uint16_t array_step ; // Deliberate use of 16-bit to balance speed and size
145+ uint16_t array_size ; // Deliberate use of 16-bit to balance speed and size
146+ uint16_t type ; // Deliberate use of 16-bit to balance speed and size
124147
125148} param_t ;
126149
@@ -149,16 +172,55 @@ typedef struct param_s {
149172#define PARAM_TIMESTAMP_INIT (_name )
150173#endif
151174
175+ static const uint16_t node_self = 0 ;
176+
177+ #define STR1 (x ) #x
178+ #define STR (x ) STR1(x)
179+
180+ /*
181+ * Compile-time type check for param backing storage.
182+ *
183+ * This macro verifies that the C type of the object pointed to by `ptr`
184+ * matches the PARAM_TYPE_* token specified by `_ptype_token`.
185+ *
186+ * It uses the GCC/Clang builtin `__builtin_types_compatible_p(T1, T2)`,
187+ * which evaluates to 1 at compile time if the two types are compatible
188+ * (i.e. the same underlying type), and 0 otherwise.
189+ *
190+ * The result is stored in a uniquely named enum constant to force the
191+ * expression to be an integer constant expression, allowing it to be
192+ * used in a `_Static_assert`.
193+ *
194+ * If the types do not match, compilation fails with a clear error message,
195+ * preventing mismatches such as declaring PARAM_TYPE_UINT16 for a uint8_t
196+ * variable, or passing the address of an array instead of an element.
197+ *
198+ * Notes:
199+ * - This check is evaluated entirely at compile time.
200+ * - No code or data is generated.
201+ * - Requires GCC or Clang (GNU extensions enabled).
202+ */
203+ #define PARAM_TYPECHECK (_name , _ptype_token , ptr ) \
204+ enum { \
205+ param_typecheck__##_name = __builtin_types_compatible_p( \
206+ __typeof__(*(ptr)), PARAM_CTYPE(_ptype_token) \
207+ ) \
208+ }; \
209+ _Static_assert(param_typecheck__##_name, \
210+ "param: param_type does not match pointer element type")
211+
212+
152213
153214#define PARAM_DEFINE_STATIC_RAM (_id , _name , _type , _array_count , _array_step , _flags , _callback , _unit , _physaddr , _docstr ) \
154- ; /* Catch const param defines */ \
215+ _Static_assert(((_array_count) <= 1) ? ((_array_step) <= 0) : ((_array_step) >= PARAM_SIZEOF(_type)), "param: array_step invalid for array_count"); \
216+ _Static_assert(((_array_count) <= 1) ? 1 : (((_array_step) % PARAM_ALIGNOF(_type)) == 0U),"param: array_step not aligned to type"); \
217+ PARAM_TYPECHECK(_name, _type, _physaddr); \
155218 PARAM_TIMESTAMP_DECL(_name) \
156- uint16_t _node_ ##_name = 0 ; \
157- __attribute__((section ("param" ))) \
158- __attribute__((used , no_reorder )) \
159- param_t _name = { \
219+ __attribute__((section("param." STR(_name)))) \
220+ __attribute__((used, aligned(8))) \
221+ const param_t _name = { \
160222 .vmem = NULL, \
161- .node = & _node_ ## _name , \
223+ .node = (uint16_t *) &node_self , \
162224 .id = _id, \
163225 .type = _type, \
164226 .name = #_name, \
@@ -174,13 +236,13 @@ typedef struct param_s {
174236 }
175237
176238#define PARAM_DEFINE_STATIC_VMEM (_id , _name , _type , _array_count , _array_step , _flags , _callback , _unit , _vmem_name , _vmem_addr , _docstr ) \
177- ; /* Catch const param defines */ \
239+ _Static_assert(((_array_count) <= 1) ? ((_array_step) <= 0) : ((_array_step) >= PARAM_SIZEOF(_type)), "param: array_step invalid for array_count"); \
240+ _Static_assert(((_array_count) <= 1) ? 1 : (((_array_step) % PARAM_ALIGNOF(_type)) == 0U),"param: array_step not aligned to type"); \
178241 PARAM_TIMESTAMP_DECL(_name) \
179- uint16_t _node_ ##_name = 0 ; \
180- __attribute__((section ("param" ))) \
181- __attribute__((used , no_reorder )) \
182- param_t _name = { \
183- .node = & _node_ ##_name , \
242+ __attribute__((section("param." STR(_name)))) \
243+ __attribute__((used, aligned(8))) \
244+ const param_t _name = { \
245+ .node = (uint16_t *) &node_self, \
184246 .id = _id, \
185247 .type = _type, \
186248 .name = #_name, \
@@ -199,11 +261,13 @@ typedef struct param_s {
199261#define PARAM_REMOTE_NODE_IGNORE 16382
200262
201263#define PARAM_DEFINE_REMOTE (_id , _name , _nodeaddr , _type , _array_count , _array_step , _flags , _physaddr , _docstr ) \
202- ; /* Catch const param defines */ \
264+ _Static_assert(((_array_count) <= 1) ? ((_array_step) <= 0) : ((_array_step) >= PARAM_SIZEOF(_type)), "param: array_step invalid for array_count"); \
265+ _Static_assert(((_array_count) <= 1) ? 1 : (((_array_step) % PARAM_ALIGNOF(_type)) == 0U),"param: array_step not aligned to type"); \
266+ PARAM_TYPECHECK(_name, _type, _physaddr); \
203267 PARAM_TIMESTAMP_DECL(_name) \
204- __attribute__((section ("param" ))) \
205- __attribute__((used , no_reorder )) \
206- param_t _name = { \
268+ __attribute__((section("param." STR(_name) ))) \
269+ __attribute__((used, aligned(8) )) \
270+ const param_t _name = { \
207271 .node = _nodeaddr, \
208272 .id = _id, \
209273 .type = _type, \
@@ -219,7 +283,9 @@ typedef struct param_s {
219283 };
220284
221285#define PARAM_DEFINE_REMOTE_DYNAMIC (_id , _name , _node , _type , _array_count , _array_step , _flags , _physaddr , _docstr ) \
222- ; /* Catch const param defines */ \
286+ _Static_assert(((_array_count) <= 1) ? ((_array_step) <= 0) : ((_array_step) >= PARAM_SIZEOF(_type)), "param: array_step invalid for array_count"); \
287+ _Static_assert(((_array_count) <= 1) ? 1 : (((_array_step) % PARAM_ALIGNOF(_type)) == 0U),"param: array_step not aligned to type"); \
288+ PARAM_TYPECHECK(_name, _type, _physaddr); \
223289 PARAM_TIMESTAMP_DECL(_name) \
224290 uint16_t _node_##_name = _node; \
225291 param_t _name = { \
@@ -239,8 +305,8 @@ typedef struct param_s {
239305
240306/* Native getter functions, will return native types */
241307#define PARAM_GET (type , name ) \
242- type param_get_##name(param_t * param); \
243- type param_get_##name##_array(param_t * param, unsigned int i);
308+ type param_get_##name(const param_t * param); \
309+ type param_get_##name##_array(const param_t * param, unsigned int i);
244310PARAM_GET (uint8_t , uint8 )
245311PARAM_GET (uint16_t , uint16 )
246312PARAM_GET (uint32_t , uint32 )
@@ -255,10 +321,10 @@ PARAM_GET(double, double)
255321
256322/* Native setter functions, these take a native type as argument */
257323#define PARAM_SET (type , name ) \
258- void param_set_##name(param_t * param, type value); \
259- void param_set_##name##_nocallback(param_t * param, type value); \
260- void param_set_##name##_array(param_t * param, unsigned int i, type value); \
261- void param_set_##name##_array_nocallback(param_t * param, unsigned int i, type value);
324+ void param_set_##name(const param_t * param, type value); \
325+ void param_set_##name##_nocallback(const param_t * param, type value); \
326+ void param_set_##name##_array(const param_t * param, unsigned int i, type value); \
327+ void param_set_##name##_array_nocallback(const param_t * param, unsigned int i, type value);
262328PARAM_SET (uint8_t , uint8 )
263329PARAM_SET (uint16_t , uint16 )
264330PARAM_SET (uint32_t , uint32 )
@@ -272,24 +338,24 @@ PARAM_SET(double, double)
272338#undef PARAM_SET
273339
274340/* Non-native types needs to go through a function which includes a void pointer and the length */
275- void param_set_data (param_t * param , const void * inbuf , int len );
276- void param_set_data_nocallback (param_t * param , const void * inbuf , int len );
277- void param_get_data (param_t * param , void * outbuf , int len );
278- void param_set_string (param_t * param , const char * inbuf , int len );
341+ void param_set_data (const param_t * param , const void * inbuf , int len );
342+ void param_set_data_nocallback (const param_t * param , const void * inbuf , int len );
343+ void param_get_data (const param_t * param , void * outbuf , int len );
344+ void param_set_string (const param_t * param , const char * inbuf , int len );
279345#define param_get_string param_get_data
280346
281347/* Generic setter function:
282348 * This function can be used to set data of any type
283349 */
284- void param_set (param_t * param , unsigned int offset , void * value );
285- void param_get (param_t * param , unsigned int offset , void * value );
350+ void param_set (const param_t * param , unsigned int offset , void * value );
351+ void param_get (const param_t * param , unsigned int offset , void * value );
286352
287353/* Returns the size of a native type */
288354int param_typesize (param_type_e type );
289- int param_size (param_t * param );
355+ int param_size (const param_t * param );
290356
291357/* Copies from one parameter to another */
292- void param_copy (param_t * dest , param_t * src );
358+ void param_copy (const param_t * dest , const param_t * src );
293359
294360/* External hooks to get atomic writes */
295361extern __attribute__((weak )) void param_enter_critical (void );
0 commit comments