svd2pac 0.7.0

Tool to generate peripheral access crates from SVD files
Documentation
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
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# svd2pac


Tool to generate Peripheral Access Crates from SVD files

This tool has a very different approach compared to `svd2rust` because our requirements are different and are quite similar to [chiptool](https://github.com/embassy-rs/chiptool).

## Major Requirements


- Register access should be unsafe because we consider akin to C FFI.
  Inherent undefined behavior should be dealt with at the driver layers, trying to handle safety in the PAC often does either not help or make it hard to use.
  (e.g. sometimes also the order of writing register bitfields is important.
  discussion on this topic available here <https://github.com/rust-embedded/svd2rust/issues/714>).
- No ownership because owned registers are an obstacle to writing low level drivers (LLD). Anyway writing
  LLDs requires a lot of unsafe code and ownership makes it more complex to access registers from interrupts
  and other threads. LLDs shall present safe APIs because only they can implement all logic for a safe usage of peripherals.
  Moreover for many peripherals the splitting of peripheral is smaller unit is not obvious and depends on use cases.
- Support [tracing]#tracing-feature of register accesses and additionally mocking of registers on non-embedded devices through
  external libraries. This allows the execution unit tests for code that uses the generated libraries on non-embedded devices.
- No macros. Absence of macros make easier the debugging.
- PAC shall have 0 dependencies to any other crates.
  - Exception: `--target=cortex-m`. In this case the generated PAC has some dependencies in order to be usable in ARM Cortex Rust ecosystem.
- Use associated constants instead of `Enum` for bitfield values so users can easily create new values.
  Enumerations constrain the possible values of a bitfield but many times the SVD enum description has missing enumeration values.
  There are multiple reasons:

- Too many values for documentation/SVD.
  - Valid values depend on other register values or conditions so documentation writers could decided to not list them in the SVD.
  - Lazyness of user manual writer. (Sorry but it sometimes happens ;-))

## Known Limitations


* Inheritance via `derivedFrom` attribute is presently not supported for bitfields declaration.
  Moreover in the case that a parent is an element of an array, inheritance can only refer to the first element.
* `resetMask` tag is ignored
* `protection` tag is ignored
* `writeConstraint` tag is ignored
* `modifiedWriteValues` tag is ignored
* `readAction` tag is ignored
* `headerEnumName` tag is ignored
* in `enumeratedValue` only `value` tag is supported. No support for _don't care bits_ and `isDefault` tag
* `alternateGroup` is ignored therefore it is not possible to have two registers with same name.

## How to install & prerequisite


```bash
cargo install svd2pac
```

if automatic code formatting is desired install `rustfmt`.

```bash
rustup component add rustfmt
```

## How to use the tool


Get a full overview for all cli flags:
```bash
svd2pac -h
```

Generate PAC without any platform specific code:
```bash
svd2pac <your_svd_file> <target directory>
```

To generated Aurix PACs use:
```bash
svd2pac --target aurix <your_svd_file> <target directory>
```

By default `svd2pac` performs strict validation of svd files.

It is possible to relax or disable svd validation by using option `--svd-validation-level`
```bash
svd2pac --svd-validation-level weak <your_svd_file> <target directory>
```
### Notable CLI flags


---
#### Select target :`--target` option

This option allows to have target specific code generation

##### `--target=generic`

This target allows generation of generic code that is independent from any architecture.
It ignores  nvicPrioBits, fpuPresent,mpuPresent, vendorSystickConfig attributes and interrupt tag.

##### `--target=aurix`


Generate the PAC with Aurix platform specific `lmst` instruction support in addition to
normal `read/write` instructions.

##### `--target=cortex-m`


The purpose of this option is generating a PAC that can be used with common cortex-m framework as RTIC.
Developer can use CPU register with same API generated by `svd2rust` but for peripheral he shall use the API of `svd2pac`
In this way he can reuse the code related to CPU and develop peripheral driver using `svd2pac` style.

Extra feature compared to `generic` target

- Re-export of cortex-m core peripherals
- Peripherals type but now it is possible to call Peripheral::take without limitations.
- Interrupt table
---
#### Enable register mocking: `--tracing` option
Enable with the `--tracing` cli flag.
Generate the PAC with a non-default feature flag to allow for tracing reads/writes, [see below]#tracing-feature

### Environment variables


- `SVD2PAC_LOG_LEVEL` sets the log level (see [log]https://docs.rs/log/0.4.21/log/enum.LevelFilter.html)
- `SVD2PAC_LOG_STYLE` sets whether or not to print styles with records (see [env_logger]https://docs.rs/env_logger/latest/env_logger/fmt/enum.WriteStyle.html)

## How to use the generated code


The generator outputs a complete crate into the provided folder.
In the generated PACs all peripherals modules are gated by a feature and therefore by default no peripheral modules is compiled.
This is speed-up the compilation process. The `features=["all"]` enable the compilations of all modules.

### Naming


Some examples showing naming/case, given the timer module in `test_svd/simple.xml`:
- `TIMER` instance of a module struct for a peripheral called "timer"
- `timer::Timer` type of the module instance above
- `TIMER::bitfield_reg()` access function for a register
- `timer::bitfield_reg` module containing bitfield structs for the "BITFIELD_REG" register
- `timer::bitfield_reg::Run` module containing enumeration values for the "RUN" bitfield
- `timer::bitfield_reg::Run::RUNNING` bitfield value constant

### Examples


>**Note**
>
>The following examples are based on the `test_svd/simple.xml` svd used for testing.
>In this example we mostly use a `TIMER` module with a few registers, among them:
>- `SR` a status register that is mostly read-only
>- `BITFIELD_REG` which is a register with multiple bitfields
>- `NONBITFIELD_REG`, a register without bitfields

#### Read


A register is read using the `.read()` function. It returns a struct with convenient functions to access
bitfield values. Each bitfield is represented by a struct that is optimized away, the actual values can
be retrieved by calling `.get()`

```rust
use test_pac::{timer, TIMER};

// Read register `SR` and check `RUN` bitfield
let status = unsafe { TIMER.sr().read() };
if status.run().get() == timer::sr::Run::RUNNING { /* do something */ }

// Access bitfield directly inline
while unsafe { TIMER.sr().read().run().get() == timer::sr::Run::RUNNING } { /* ... */ }

// Check bitfield with enumeration
// (r# as prefix must be used here since `match` is a rust keyword)
match unsafe { TIMER.sr().read().r#match().get() } {
    timer::sr::Match::NO_MATCH => (),
    timer::sr::Match::MATCH_HIT => (),
    // since .get() returns a struct, match does not recognize
    // an exhaustive match and a wildcard is needed
    _ => panic!("impossible"),
}

// a register might not have a bitfield at all, then we access the value directly
let numeric_value = unsafe { TIMER.prescale_rd().read() };
```

#### Modify (read/modify/write)


The `modify` function takes a closure/function that is passed to the current register value.
The closure must modify the passed value and return the value to be written.

```rust
use test_pac::{timer, TIMER};

// read `BITFIELD_REG` register, set `BoolRw` to true, `BitfieldRw` to 0x3,
// then write back to register
unsafe {
    TIMER
        .bitfield_reg()
        .modify(|r| r.boolrw().set(true).bitfieldrw().set(0x3))
}

// write with dynamic value and enum
let x: bool = get_some_bool_value();
unsafe {
    TIMER.bitfield_reg().modify(|r| {
        r.bitfieldenumerated()
            .set(timer::bitfield_reg::BitfieldEnumerated::GPIOA_6)
            .boolrw()
            .set(x)
    })
}
```

> Note: The register is not modified when the `set()` function is called. `set()` modifies the value
> stored in the CPU and returns the modified struct. The register is only written once with
> the value returned by the closure.
>
> Note: `modify()`, due to doing a read and write with modification of read data in between is not
> atomic and can be subject to race conditions and may be interrupted by an interrupt.

#### Write


A register can be written with an instance of the appropriate struct. The struct instance can be obtained
from a read by calling `.default()` (to start off with the register default value) or from a previous
register read/write.

```rust
use test_pac::{timer, TIMER};

// start with default value, configure some stuff and write to
// register.
let reg = timer::BitfieldReg::default()
    .bitfieldrw()
    .set(1)
    .boolw()
    .set(true);
unsafe { TIMER.bitfield_reg().write(reg) };

/* do some other initialization */

// set `BoolRw` in addition to the settings before and write that
// note that .set() returns a new instance of the BitfieldReg struct
// with the old being consumed
// additional changes could be chained after .set() as above
let reg = reg.boolrw().set(true);
unsafe { TIMER.bitfield_reg().write(reg) };
```

#### Initialization & write-only registers


`.init()` allows for the same functionality as `.write()`, but it is limited to start with the register
default value. It can also be used as a shorthand for write-only registers.

The closure passed to the `.init()` function gets the default value as input and writes back
the return value of the closure to the register.

```rust
use test_pac::{timer, TIMER};

// do some initializations, write `BoolW` and `BoolRW` with given values,
// write others with defaults
unsafe {
    TIMER
        .bitfield_reg()
        .init(|r| r.boolw().set(true).boolrw().set(false))
}

// use init also for write-only registers
unsafe {
    TIMER.int().init(|r| {
        r.en()
            .set(timer::int::En::ENABLE)
            .mode()
            .set(timer::int::Mode::OVERFLOW)
    })
};
```
#### Combine all the things

Especially the read and write functionality can be combined, e.g.

```rust
let status = unsafe { TIMER.bitfield_reg().read() };
if status.boolr().get() {
    let modified = status.boolrw().set(true);
    unsafe { TIMER.bitfield_reg().write(modified) }
}
```

#### Raw access

For use cases like logging, initializing from a table, etc. it is
possible to read/write registers as plain integers.

```rust
// get register value as integer value
let to_log = unsafe { TIMER.sr().read().get_raw() };

// write register with opaque integer value
unsafe { TIMER.bitfield_reg().write_raw(0x42dead42) };

// write register with opaque integer value, e.g. read from table
// `set_raw` can be mixed with other bitfield setting functions
unsafe { TIMER.bitfield_reg().modify(|r| r.set_raw(0x1234)) };
```

#### Modify Atomic (only Aurix)

This function is available only for Aurix microcontrollers. It uses the  `ldmst` instruction
to read-modify-write a value in a register. This instruction blocks the bus until the end of
the transaction. Therefore it affects the other masters on the bus.

```rust
use test_pac::{timer, TIMER};
TIMER.bitfield_reg().modify_atomic(|f| {
    f.bitfieldenumerated()
        .set(timer::bitfield_reg::BitfieldEnumerated::GPIOA_0)
        .bitfieldw()
        .set(3)
});
```
Code generation for Aurix is enabled using `--target aurix `

#### Array of peripherals


SVD arrays of peripherals are modeled using Rust arrays.

```rust
use test_pac::{UART,uart};
for peri in UART {
    unsafe {
        peri.reg16bitenum().modify(|r| {
            r.bitfield9bitsenum()
                .set(uart::reg16bitenum::Bitfield9BitsEnum::VAL_0)
        })
    };
}
```

#### Array of registers

Arrays of registers are modeled as an array of register structs in the module.

```rust
use test_pac::*;
let reg_array = TIMER.arrayreg();
for reg in reg_array {
    let reg_val = unsafe { reg.read() };
    let old_val = reg_val.get();
    unsafe { reg.write(reg_val.set(old_val + 1)) };
}
```

#### Array of bitfields

Arrays of bitfields are modeled as an array of bitfield structs in the register.

```rust
 let mut reg_value = unsafe { TIMER.bitfield_reg().read() };
 for x in 0..2 {
    reg_value = reg_value.fieldarray(x).set(timer::bitfield_reg::FieldArray::FALLING);
 }
 unsafe { TIMER.bitfield_reg().write(reg_value) };
```

#### Write an enumerated bitfield by passing an integer literal

The size of value cannot exceed bit field size.
Here the associated struct type can be created from the integer,
as the `From` trait implementation is available for the bitfield structure.

```rust
use test_pac::{timer, TIMER};
unsafe {
    TIMER.bitfield_reg().modify(|f| {
        f.bitfieldenumerated()
            .set(0.into())
    });
}
```

#### Get mask and offset of a bitfield

It is possible to get mask and offset of a single bitfield using `mask` and `offset`. The returned mask is aligned to the LSB and not shifted (i.e. a 3-bit wide field has a mask of `0x7`, independent of position of the field).
```rust
 use test_pac::{timer, TIMER};
 unsafe {
    let register_bitfield = TIMER.bitfield_reg().read().bitfieldr();
    let _offset = register_bitfield.offset();
    let _mask = register_bitfield.mask();
}
```

## Tracing feature

When generating the PAC with the `--tracing` cli-flag, the PAC is generated with
an optional feature flag `tracing`. Enabling the feature provides the following
additional functionalities:
- an interface where register accesses can be piped though, enabling
  developers to log accesses to registers or even mock registers outright.
  An implementaion of that interface is provided by [`regmock-rs`]https://github.com/Infineon/regmock-rs.
- a special `RegisterValue` trait that allows constructing values of
  registers from integers.
- an additional `insanely_unsafe` module which allows reading and writing, write-only and
  read-only registers (intended for mocking state in tests).
- an additional `reg_name` module that contains a perfect hash map of physical
  addresses to string names of all registers that reside at an address.

### Examples

Below, some simple examples on how to use the tracing APIs are shown.
For a complete example of how to use the tracing features for
e.g. unittesting see the documentation of [`regmock-rs`](https://github.com/Infineon/regmock-rs).

#### Construcing a register value from a raw value with tracing

When implementing tests using the tracing feature we want to be
able to provide arbitrary data during those tests.

```rust
use pac::common::RegisterValue;
let value = pac::peripheral::register::new(0xC0FFEE);
unsafe{ pac::PERIPHERAL.register().write(value) };
```

#### Reading a value from a **write-only** register with tracing

Again for testing: in a testcase we need to do the exact opposite
of what normal code does, i.e. we need to "write" read-only registers
and "read" write-only registers.

Tracing provides a backdoor to allow those actions that are not
allowed in normal code.

```rust
use pac::tracing::insanely_unsafe;
let value = unsafe{ pac::PERIPHERAL.write_only_register().read_write_only() };
```

#### Get the names of registers at a specific address

For better logging a map of address to name translation is generated/available
if tracing is enabled.

```rust
let regs_at_c0ffee = pac::reg_name::reg_name_from_addr(0xC0FFEE);
println!("{regs_at_c0ffee:?}");
```

## How to use in your `build.rs`


It is possible to generate the PAC during the build of an application by calling [`main`] or [`main_parse_arguments`].

## Running tests


To execute the tests it is required to add as target "thumbv7em-none-eabihf".
This can be done using
```bash
rustup target add thumbv7em-none-eabihf
```

To test the generation of Aurix PAC it is necessary to install Hightec Rust Aurix compiler and select it as default compiler.
`build.rs` detects automatically the toolchain and add the configuration option to enable Aurix specific tests.

## Credits


A small portion of template common.tera is copied from
[Link to commit from where code has been copies](https://github.com/embassy-rs/chiptool/commit/4834a9b35982b393beae84a04960c9add886c1f7)

License: MIT