-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathrloop_code_standard.txt
432 lines (313 loc) · 9.06 KB
/
rloop_code_standard.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
Basic rLoop Code Standard
Original Author: Lachlan Grogan (SafetyLok)
*******************************************
DIRECTORIES
----------------------------------------
/FIRMWARE is the main firmware directory
/COMMON_CODE contains code which is not specific to the project
/COMMON_CODE/RM4 contains RM48 silicon driver libs
/COMMON_CODE/MULTICORE contains various device drivers / middle ware
/PROJECT_CODE contains specific modules for RLOOP
/VERIFICATION contains verification test projects for each PROJECT_CODE module
/BOARD_SUPPORT contains the board support header files.
MODULE PART NUMBERS
----------------------------------------
Each module is in the format of LCCMXXX__<PROJECT>__<DESCRIPTION>
For example LCCM655 is the flight control unit core processing code.
If you need new parts numbers, please see lachlan.
FIRMWARE PART NUMBERS
----------------------------------------
All firmware has a part number in the format of LFWXXX__<PROJECT>__<DESCRIPTION>
Firmware is tracked by the main part number for example LFW513 which is the power node firmware.
*******************************************
FILE STRUCTURES
----------------------------------------
Each LCCMXXX module can contain sub modules which are located in sub folders. For example:
LCCM653__RLOOP__POWER_CORE
LCCM653__RLOOP__POWER_CORE\BMS for the battery manangement system interface layer.
It is recommended that software is layered into sub layers (sub modules). The smaller the module, the easier to test.
Sub layers also work well with doxygen.
FILE NAMES
----------------------------------------
File names should represent the module and sub module, be in lower case, and separated by a '__'
Example power_core__bms.c for the POWER CORE / BMS Layer
FILE HEADERS
----------------------------------------
File headers are vital to give a brief of the file and help doxygen identify the file.
Example (Copy and paste this into your new file)
/**
* @file POWER_CORE__BMS.C
* @brief Battery Management Interface Layer
* @author Lachlan Grogan
* @copyright rLoop Inc.
* @st_fileID
*/
Take note of the layout:
1. There is a '/**' on a single line
2. There is a ' * ' on each line
3. There is a @ symbol for each tag that doxygen will use.
4. The header is closed with a ' */'
Note: The file name must match exactly the same as the file, else doxygen will skip.
DATA TYPES
----------------------------------------
The only data types available in rLoop are:
Lint8
Luint8
Lint16
Luint16
Lint32
Luint32
Lint64*
Luint64*
Lfloat32
Lfloat64*
*Please use these types with caution.
VARIABLE NAMES
----------------------------------------
Variables (and function parameters) must be prefixed with the data type.
Example:
Luint8 u8Counter;
This clearly indicates to a reviewer that the data type is a unsigned 8bit value.
BAD Example:
Luint8 i;
for(i = 0; i < 300; i++){};
The reviewer is unable to determine the data type and thefore can't determine that 300 is greater than the range of data available.
Avoid i, j, k
It is better to use
u32ArrayCounter, u32BufferIndex, s16FilterCoeff as these are easier to review.
COMMENTS
----------------------------------------
1. In general each line of code should have a comment. The comments should read like a book if the code was removed.
For example:
void vFoo(Luint8 u8X)
{
//check if X is less than 10
if(u8X < 10U)
{
//yes its less than 10, so call the lookup table
vLUT__Process();
}
else
{
//the value was greater or equal to ten, do no processing.
}
}
In the above example each comment has a // and is directly on the line above the source line
2. Avoid using /* COMMENT */ in your code as this can induce nested comments.
3. For doxygen to pickup on enum / structure elements, use a /** Description */ only
4. Some bad example would be:
void vFoo(Luint8 u8X)
{
vSomeComplexFunction();
vSomeMoreFunctions();
while(*n++=*i++);
}
The above function is not commented at all.
void vFoo(Luint8 u8X)
{
vSomeComplexFunction(); /* Do this complex function */
vSomeMoreFunctions(); //mixing types of comments way off the page
while(*n++=*i++);
//this does stuff
}
The above example does not allow a reader to follow the text very well.
One good example of how a reviewer can catch an error.
...
//Increment the counter
u8Counter--;
FUNCTION NAMES
----------------------------------------
Function names must be self documenting in that:
1. Must be prefixed with the return data type (v = void, u8 = unsigned 8, etc.)
2. Must contain an upper case of the source module
3. Must contain an upper case of the sub module (if any)
4. Must contain a '__'
5. Must contain a description of the function they are implementing.
Examples:
Lfloat32 f32POWERNODE_BMS__Get_CellVoltage(...);
void vPOWERNODE_BATTTEMP__Start_Sensor_Scan();
The above functions are self documenting.
BAD Examples:
GetCellVoltage()
cellVolts()
Volts()
GetCV()
The reviwer in the above cases would not be able to determine return types, source modules, etc.
FUNCTION DOCUMENTATION
----------------------------------------
To assist with documentation each function must have a doxygen function header:
Please copy:
/***************************************************************************//**
* @brief
* Insert your brief here
*
* @param[in/out] ParamName Param Details
* @return Information on return types (if any)
*/
Please note the layout of the header. Its important that this format is kept.
There are other tags such as @note, @image, etc. that can be used too.
TABS, SPACES and OTHER
----------------------------------------
There is always debates about this so..
1. Tabs to be used, no spaces
2. All { and } brackets must be on new lines
*******************************************
CODE SAFETY REQUIREMENTS
----------------------------------------
The following basic requirements are to be implemented.
1. No dynamic objects. You can't use Malloc() or New(). There is no heap space in the CPU
2. NO use of the word GOTO, No unconditional jumps allowed.
3. Each function that returns a value must have exactly one return point:
Example:
Luint8 u8Foo(Luint8 u8X)
{
Luint8 u8Return;
if(u8X > 10)
{
u8Return = 1U;
}
else
{
u8Return = 0U;
}
return u8Return;
}
as opposed to:
Luint8 u8Foo(Luint8 u8X)
{
if(u8X > 10)
{
return 1;
}
else
{
return 0;
}
}
The reason for doing this is so as the reviewer can follow the code more easily and so as the static checker (PCLINT)
can check that the return value has been set on all branchs in the code.
4. If, #if, switch statements always must have a final clause.
Example:
void vFoo(Luint8 u8X)
{
switch(u8X)
{
case 0U:
//do something
break;
default:
//log the fault, etc.
break;
}
}
This is also important in the following situation
void vSetLightState(Luint8 u8State)
{
if(u8State == 1U)
{
LED_ON = 1U;
}
}
In the above example if the led was off, and you set u8State to 1 the led would switch on.
BUT. If the led was already on and you set u8State to 0, the led would not switch off.
5. NO compound statements.
Example of what I consider compound statments
...
if(u8GetSomeValue() && u8GetAnotherValue())
{
//do something.
}
This is very difficult to debug. A better approach would be:
...
Luint8 u8A;
Luint8 u8B;
Luint8 u8C;
u8A = u8GetSomeValue();
u8B = u8GetAnotherValue();
u8C = u8A & u8B;
if(u8C != 0U)
{
//do something.
}
It might look like more work, but in the above good example it is very easy to debug, set breakpoints, etc.
Other bad examples are:
u16Temp == u16A ? u16B : u16C;
6. NO Floating Point Equality
You must not do this.
void vFoot(Lfloat32 f32Value)
{
if(f32Value == 4.0F)
{
//do something.
}
}
Due to the nature of floating points you can't have equality.
A better way would be:
void vFoot(Lfloat32 f32Value)
{
if(f32Value >= 4.0F)
{
//do something.
}
}
Or make a tolerance function
7. All variables must be initialised by the programmer.
Example:
void vFoo(void)
{
Luint8 u8Temp;
if(u8Temp == 0U)
{
}
}
You must do something like:
void vFoo(void)
{
Luint8 u8Temp;
u8Temp = 0U;
if(u8Temp == 0U)
{
}
}
You also must never rely on the C_INIT functions to initialse your data.
8. Limit local memory usage.
Example:
void vFoo(void)
{
Luint32 u32Array[512];
}
The array is created on the stack, which is limited to a few Kb.
Also limit taking the address of local variables, unless totally needed.
Example:
void vFoo(void)
{
Luint8 u8Buffer[8];
//address of local.
vBar(&u8Buffer[0]);
}
9. NO Magic Numbers:
Example:
void vFoo(void)
{
Luint8 u8Counter;
Luint32 u32Array[10];
for(u8Counter = 0U; u8Counter < 20U; u8Counter++)
{
u32Array[u8Counter] = 1U;
}
}
//the above example corrupts the stack.
You should always @define array sizes.
10. Always check array bounds on memory writes.
Example:
void vFoo(Luint8 u8Index)
{
if(u8Index < C_MAX_ARRAY_SIZE)
{
u8Array[u8Index] = 1U;
}
else
{
//log the error
}
}