Skip to content

Commit a0f66f8

Browse files
author
Jorge Aparicio
committed
document the API that svd2rust generates
1 parent 48ed793 commit a0f66f8

File tree

1 file changed

+171
-0
lines changed

1 file changed

+171
-0
lines changed

README.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,177 @@ pub struct Rcc {
3838
(..)
3939
```
4040

41+
## API
42+
43+
The `svd2rust` generates the following API for each peripheral:
44+
45+
### Register block
46+
47+
A register block "definition" as a `struct`. Example below:
48+
49+
``` rust
50+
/// Inter-integrated circuit
51+
#[repr(C)]
52+
pub struct I2c1 {
53+
/// 0x00 - Control register 1
54+
pub cr1: Cr1,
55+
/// 0x04 - Control register 2
56+
pub cr2: Cr2,
57+
/// 0x08 - Own address register 1
58+
pub oar1: Oar1,
59+
/// 0x0c - Own address register 2
60+
pub oar2: Oar2,
61+
/// 0x10 - Timing register
62+
pub timingr: Timingr,
63+
/// 0x14 - Status register 1
64+
pub timeoutr: Timeoutr,
65+
/// 0x18 - Interrupt and Status register
66+
pub isr: Isr,
67+
/// 0x1c - Interrupt clear register
68+
pub icr: Icr,
69+
/// 0x20 - PEC register
70+
pub pecr: Pecr,
71+
/// 0x24 - Receive data register
72+
pub rxdr: Rxdr,
73+
/// 0x28 - Transmit data register
74+
pub txdr: Txdr,
75+
}
76+
```
77+
78+
The user has to "instantiate" this definition for each peripheral instance. They have several
79+
choices:
80+
81+
- `static`s and/or `static mut`s. Example below:
82+
83+
``` rust
84+
extern "C" {
85+
// I2C1 can be accessed in read-write mode
86+
pub static mut I2C1: I2c;
87+
// whereas I2C2 can only be accessed in "read-only" mode
88+
pub static I2C1: I2c;
89+
}
90+
```
91+
92+
Where the addresses of these register blocks must be provided by a linker script:
93+
94+
``` ld
95+
/* layout.ld */
96+
I2C1 = 0x40005400;
97+
I2C2 = 0x40005800;
98+
```
99+
100+
This has the side effect that the `I2C1` and `I2C2` symbols get "taken" so no other C/Rust symbol
101+
(`static`, `function`, etc.) can have the same name.
102+
103+
- "constructor" functions. Example, equivalent to the `static` one, below:
104+
105+
``` rust
106+
// Addresses of the register blocks. These are private.
107+
const I2C1: usize = 0x40005400;
108+
const I2C2: usize = 0x40005800;
109+
110+
// NOTE(unsafe) can alias references to mutable memory
111+
pub unsafe fn i2c1() -> &'mut static I2C {
112+
unsafe { &mut *(I2C1 as *mut I2c) }
113+
}
114+
115+
pub fn i2c2() -> &'static I2C {
116+
unsafe { &*(I2C2 as *const I2c) }
117+
}
118+
```
119+
120+
### `read` / `modify` / `write`
121+
122+
Each register in the register block, e.g. the `cr1` field in the `I2c` struct, exposes a combination
123+
of the `read`, `modify` and `write` methods. Which methods exposes each register depends on whether
124+
the register is read-only, read-write or write-only:
125+
126+
- read-only registers only expose the `read` method.
127+
- write-only registers only expose the `write` method.
128+
- read-write registers exposes all the methods: `read`, `modify` and `write`.
129+
130+
This is signature of each of these methods:
131+
132+
(using the `CR2` register as an example)
133+
134+
``` rust
135+
impl Cr2 {
136+
pub fn modify<F>(&mut self, f: F)
137+
where F: FnOnce(&mut Cr2W) -> &mut Cr2W
138+
{
139+
..
140+
}
141+
142+
pub fn read(&self) -> Cr2R { .. }
143+
144+
pub fn write(&mut self, value: Cr2W) { .. }
145+
}
146+
```
147+
148+
The `read` method performs a single, volatile `LDR` instruction and returns a proxy `Cr2R` struct
149+
which allows access to only the readable bits (i.e. not to the reserved bits) of the `CR2` register:
150+
151+
``` rust
152+
impl Cr2R {
153+
/// Bit 0 - Slave address bit 0 (master mode)
154+
pub fn sadd0(&self) -> bool { .. }
155+
156+
/// Bits 1:7 - Slave address bit 7:1 (master mode)
157+
pub fn sadd1(&self) -> u8 { .. }
158+
159+
(..)
160+
}
161+
```
162+
163+
Usage looks like this:
164+
165+
``` rust
166+
// is the SADD0 bit of the CR2 register set?
167+
if i2c1.c2r.read().sadd0() {
168+
// something
169+
} else {
170+
// something else
171+
}
172+
```
173+
174+
The `write` method performs a single, volatile `STR` instruction to write a value to the `CR2`
175+
register. This method takes a `Cr2W` struct that only allows constructing valid states of the `CR2`
176+
register. The only constructor that `Cr2W` provides is `reset_value` which returns the value of the
177+
`CR2` register after a reset. The rest of `Cr2W` methods are "builder" like and can be used to set
178+
or reset the writable bits of the `CR2` register.
179+
180+
``` rust
181+
impl Cr2W {
182+
/// Reset value
183+
pub fn reset_value() -> Self {
184+
Cr2W { bits: 0 }
185+
}
186+
187+
/// Bits 1:7 - Slave address bit 7:1 (master mode)
188+
pub fn sadd1(&mut self, value: u8) -> &mut Self { .. }
189+
190+
/// Bit 0 - Slave address bit 0 (master mode)
191+
pub fn sadd0(&mut self, value: bool) -> &mut Self { .. }
192+
}
193+
```
194+
195+
Usage looks like this:
196+
197+
``` rust
198+
i2c1.cr2.write(*Cr2W::reset_value().sadd0(true).sadd1(0b0011110));
199+
```
200+
201+
Finally, the `modify` method performs a read-modify-write operation that involves at least a `LDR`
202+
instruction, a `STR` instruction plus extra instructions to modify the fetched value of the `CR2`
203+
register. This method accepts a closure that specifies how the `CR2` register will be modified.
204+
205+
Usage looks like this:
206+
207+
``` rust
208+
// Sets the STOP bit of the CR2 register
209+
i2c1.cr2.modify(|r| r.stop(true));
210+
```
211+
41212
## License
42213

43214
Licensed under either of

0 commit comments

Comments
 (0)