You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/post/2020-04-11-mastering-the-gnu-linker-script.md
+6-6
Original file line number
Diff line number
Diff line change
@@ -44,7 +44,7 @@ SECTIONS
44
44
exceptions.o (.isr_vector) /* This matches all .isr_vector sections in the exceptions.o input file */
45
45
}
46
46
.text : { /* This is the output section .text */
47
-
*(.text) /* This matches all .text sections in all input files */
47
+
*(.text) /* This matches all .text sections in all input files */
48
48
*(.text*) /* This matches all .text* sections in all input files */
49
49
}
50
50
}
@@ -78,14 +78,14 @@ Disassembly of section .text:
78
78
1: 4889e5 mov %rsp,%rbp
79
79
4: 90 nop
80
80
5: 5d pop %rbp
81
-
6: c3 retq
81
+
6: c3 retq
82
82
83
83
0000000000000007 <MySecondFunction>:
84
84
7: 55 push %rbp
85
85
8: 4889e5 mov %rsp,%rbp
86
86
b: b8 00000000 mov $0x0,%eax
87
87
10: 5d pop %rbp
88
-
11: c3 retq
88
+
11: c3 retq
89
89
```
90
90
91
91
However, if we build with `gcc -c -o test.o test.c -ffunction-sections` we will see the following output:
@@ -100,7 +100,7 @@ Disassembly of section .text.MyFunction:
100
100
1: 4889e5 mov %rsp,%rbp
101
101
4: 90 nop
102
102
5: 5d pop %rbp
103
-
6: c3 retq
103
+
6: c3 retq
104
104
105
105
Disassembly of section .text.MySecondFunction:
106
106
@@ -109,7 +109,7 @@ Disassembly of section .text.MySecondFunction:
109
109
1: 4889e5 mov %rsp,%rbp
110
110
4: b8 00000000 mov $0x0,%eax
111
111
9: 5d pop %rbp
112
-
a: c3 retq
112
+
a: c3 retq
113
113
```
114
114
115
115
Now each function has it's own section. This is useful if we want to control exact placement of some functions in the output file, as we can now specify the section of the function we want to place. It is also useful when used in conjunction with the`--gc-sections` option of the GNU linker, which will remove any unused input sections from the output file, thus optimizing the size of the target binary.
This would declare a 32 bit register and we would be able to map it at the specific address where the hardware register is located (let's say address 0x40002000) with the following linker definition:
Copy file name to clipboardExpand all lines: content/post/bare-metal-register-access-api.md
+12-16
Original file line number
Diff line number
Diff line change
@@ -150,7 +150,7 @@ struct StatusRegister {
150
150
151
151
There are a couple of potential issues we should be aware of though:
152
152
* The __bit order__ in a bitfield is `implementation defined`, therefore, we __need__ to know our compiler behavior to ensure that this code will behave as expected. This also means that __this code is not portable__. In most common compilers, the order of bits in the bitfield starts from the least significant bit first, so buiding with `arm-none-eabi-gcc` or `armclang` will produce correct results for this particular case.
153
-
* The __types__ a bitfield can hold are limited to __integral types and booleans__. Compilers can augment the number of supported types, but yet again, this is `implementation defined` and varies from compiler to compiler.
153
+
* The __types__ a bitfield can hold are limited to __integral types and booleans__. Compilers can augment the number of supported types, but yet again, this is `implementation defined` and varies from compiler to compiler.
154
154
155
155
Now, assumming we are not concerned with the portability of this code, we can continue building on this solution. The next obvious thing we want to do is access the whole register in one go (without needing to read bits individually). To do so, maybe we could use a union type?
156
156
@@ -279,8 +279,8 @@ class Register {
279
279
}
280
280
281
281
/**
282
-
*@brief Modifies the value of the register by running a read-modify-write cycle.
283
-
* The mod_functor is called with the read register value as an argument and
282
+
*@brief Modifies the value of the register by running a read-modify-write cycle.
283
+
* The mod_functor is called with the read register value as an argument and
284
284
* should return the desired value to be written to the register.
The `StatusRegister` class clearly states how to read, write or modify the register, using instances of type `Fields` to interact with these functions. `Fields` can either be instantiated with the value of the register or with the reset value of the register (default constructor).
388
+
The `StatusRegister` class clearly states how to read, write or modify the register, using instances of type `Fields` to interact with these functions. `Fields` can either be instantiated with the value of the register or with the reset value of the register (default constructor).
392
389
393
390
The `StatusRegister::Read` method simply returns a `Fields` instance from which we can read the state of the regsiter.
394
391
@@ -401,7 +398,7 @@ The `Fields` class also takes care of providing the correct encapsulation for th
401
398
`Fields` also provides a type-safe interface. For example, the `Fields::state` mehtod returns a `State` instance, something which was simply not possible with the previous version.
402
399
403
400
Let's have a look at the tradeoffs of this new implementation.
404
-
401
+
405
402
- **Advantages**:
406
403
- Great control over field types and encapsulation.
407
404
- Functional approach to register writes and register modification actions.
@@ -425,7 +422,7 @@ ARM created a CMSIS System View Description (SVD) specification that although in
425
422
If you want to find the SVD files for common microcontrollers you can also refer to the [CMSIS-Packs](https://developer.arm.com/tools-and-software/embedded/cmsis/cmsis-packs), a standardized way to deliver software components from ARM. SVD files will be contained inside the `.pack` files (which are essentially zip files).
426
423
427
424
## Conclusion
428
-
425
+
429
426
In this post we have examined how to control `memory-mapped peripherals` of a CPU in an embedded context by designing an API that allows to control them in a type-safe, performant, free of `undefined behavior` and autogenerated manner. The result was an API that, although quite verbose, can be easily autogenerated and is safe and easy to use in the following regards:
430
427
431
428
* Explicit. It is very easy to identify when the register is being read/written.
@@ -438,10 +435,9 @@ In this post we have examined how to control `memory-mapped peripherals` of a CP
438
435
* No `undefined behavior` or `implementation-defined behavior`.
439
436
440
437
**Note**: In this post we have seen quite a few examples of `undefined behavior` and `implementation-defined` behavior. I probably even missed some, but please, do not dismiss the importance of `undefined behavior`. Code that seems to work today might not work tomorrow or even worse, code that __seems__ to work today actually doesn't in some subtle and perverse way. If possible, I would encourage you to look into other safer language alternatives like `Rust`, but of course this is not an option for everybody for multiple reasons (legacy code, language familiarity for the team, compiler support or other factors). That's why I think as C++ developers we need to take an active role in the safety of the code we write and actively work with the best static/dynamic analysis tools at our disposal, as well as learning the intricacies of the language and being remarkably careful about safety.
441
-
438
+
442
439
## Acknowledgements
443
440
444
441
The API presented here is inspired from the [`svd2rust`](https://github.com/rust-embedded/svd2rust) project, which uses vendor SVD files to autogenerate rust code for register access known as `peripheral access crates` or `PAC`. The API defined in this article is, after all, an adapted version of the Rust code generated by the `svd2rust` project. Unfortunately, safety is not often the first concern when designing code in C++, but hopefully this article will inspire you to design safer API's, free of the `undefined behavior` which so easily creeps into C++ or C code.
445
442
446
443
I personally hope Rust will become the next language for embedded development and I see a great wave of developers already pushing for more modern and safer programming practices. For now, many of us still live in C or C++ land, but that should not mean that we cannot benefit from some of the ideas around safety and modern development that are being brought into the embedded community but the new and vibrant embedded rust community.
0 commit comments