Skip to content

Commit e0267ca

Browse files
committed
Fix a couple of typos and add clarifications
1 parent 7221564 commit e0267ca

File tree

2 files changed

+18
-22
lines changed

2 files changed

+18
-22
lines changed

content/post/2020-04-11-mastering-the-gnu-linker-script.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ SECTIONS
4444
exceptions.o (.isr_vector) /* This matches all .isr_vector sections in the exceptions.o input file */
4545
}
4646
.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 */
4848
*(.text*) /* This matches all .text* sections in all input files */
4949
}
5050
}
@@ -78,14 +78,14 @@ Disassembly of section .text:
7878
1: 48 89 e5 mov %rsp,%rbp
7979
4: 90 nop
8080
5: 5d pop %rbp
81-
6: c3 retq
81+
6: c3 retq
8282

8383
0000000000000007 <MySecondFunction>:
8484
7: 55 push %rbp
8585
8: 48 89 e5 mov %rsp,%rbp
8686
b: b8 00 00 00 00 mov $0x0,%eax
8787
10: 5d pop %rbp
88-
11: c3 retq
88+
11: c3 retq
8989
```
9090

9191
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:
100100
1: 48 89 e5 mov %rsp,%rbp
101101
4: 90 nop
102102
5: 5d pop %rbp
103-
6: c3 retq
103+
6: c3 retq
104104

105105
Disassembly of section .text.MySecondFunction:
106106

@@ -109,7 +109,7 @@ Disassembly of section .text.MySecondFunction:
109109
1: 48 89 e5 mov %rsp,%rbp
110110
4: b8 00 00 00 00 mov $0x0,%eax
111111
9: 5d pop %rbp
112-
a: c3 retq
112+
a: c3 retq
113113
```
114114

115115
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.
@@ -288,7 +288,7 @@ typedef struct {
288288
volatile unsigned int Bit31:1;
289289
} S_GPIO_REG;
290290

291-
S_GPIO_REG gpio_reg __attribute_((section(".bss.gpio_reg")));
291+
S_GPIO_REG gpio_reg __attribute__((section(".bss.gpio_reg")));
292292
```
293293
294294
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:

content/post/bare-metal-register-access-api.md

+12-16
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ struct StatusRegister {
150150
151151
There are a couple of potential issues we should be aware of though:
152152
* 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.
154154
155155
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?
156156
@@ -279,8 +279,8 @@ class Register {
279279
}
280280

281281
/**
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
284284
* should return the desired value to be written to the register.
285285
*/
286286
template<class FuncPtr, std::enable_if_t<std::is_invocable_v<FuncPtr, uint32_t>, bool> = false>
@@ -319,24 +319,24 @@ class StatusRegister : Register {
319319
*/
320320
class Fields {
321321
public:
322-
Fields() {
322+
Fields() {
323323
// Set the register temporary variable to the reset value of the register
324324
memset(&m_bits, 0, sizeof(m_bits));
325325
}
326-
explicit Fields(uint32_t value) {
326+
explicit Fields(uint32_t value) {
327327
// Type punning via memcpy is allowed in C++
328328
memcpy(&m_bits, &value, sizeof(value));
329329
}
330330

331331
inline bool busy() const { return m_bits.busy; }
332-
inline State state() const { return static_cast<State>(m_bits.busy); }
332+
inline State state() const { return static_cast<State>(m_bits.state); }
333333
inline bool frame_error() const { return m_bits.frame_error; }
334334
inline bool overflow_error() const { return m_bits.ovfl_error; }
335335

336336
inline void clear_overflow_error() { m_bits.ovfl_error = 1; }
337337
inline void clear_frame_error() { m_bits.frame_error = 1; }
338338

339-
inline uint32_t reg_value() const {
339+
inline uint32_t reg_value() const {
340340
uint32_t value = 0;
341341
memcpy(&value, &m_bits, sizeof(value));
342342
return value;
@@ -375,10 +375,7 @@ class StatusRegister : Register {
375375
}
376376
};
377377

378-
void example_reg_access() {
379-
uint32_t status_reg_mem;
380-
StatusRegister status_reg {&status_reg_mem};
381-
378+
void example_reg_access(StatusRegister& status_reg) {
382379
status_reg.modify([=](const auto& r, auto& w) {
383380
if (r.frame_error()) {
384381
w.clear_overflow_error();
@@ -388,7 +385,7 @@ void example_reg_access() {
388385
}
389386
```
390387
391-
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).
392389
393390
The `StatusRegister::Read` method simply returns a `Fields` instance from which we can read the state of the regsiter.
394391
@@ -401,7 +398,7 @@ The `Fields` class also takes care of providing the correct encapsulation for th
401398
`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.
402399
403400
Let's have a look at the tradeoffs of this new implementation.
404-
401+
405402
- **Advantages**:
406403
- Great control over field types and encapsulation.
407404
- 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
425422
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).
426423
427424
## Conclusion
428-
425+
429426
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:
430427
431428
* 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
438435
* No `undefined behavior` or `implementation-defined behavior`.
439436
440437
**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+
442439
## Acknowledgements
443440
444441
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.
445442
446443
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.
447-

0 commit comments

Comments
 (0)