Skip to content

Commit 5e37157

Browse files
authored
Improve documentation readability (#178)
Removes extra jargon and wordiness from documentation.
1 parent bf5b6ba commit 5e37157

File tree

8 files changed

+105
-122
lines changed

8 files changed

+105
-122
lines changed

Sources/MMIO/Documentation.docc/Advanced-Topics/Custom-BitFieldProjectable.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ Define projections to map hardware bit fields to meaningful Swift types.
66

77
When working with memory-mapped hardware registers, you're fundamentally manipulating raw bits. However, these bits often represent meaningful concepts: a single bit might indicate an enabled/disabled state, a 2-bit field might represent one of four operating modes, or a multi-bit field might encode a complex configuration.
88

9-
Swift MMIO provides **type projections** through the ``MMIO/BitFieldProjectable`` protocol, allowing you to map raw bit patterns to semantically meaningful Swift types. While Swift MMIO includes built-in projections for common types like `Bool` and integer types, creating your own custom projections offers several key benefits:
9+
Swift MMIO provides **type projections** through the ``MMIO/BitFieldProjectable`` protocol, allowing you to map raw bit patterns to semantically meaningful Swift types. Swift MMIO includes built-in projections for common types like `Bool` and integer types and creating your own custom projections lets you:
1010

11-
- **Improved type safety**: Replace magic numbers with strongly-typed values
12-
- **Better code readability**: Express hardware states with meaningful names
13-
- **Compile-time validation**: Catch errors at compile time rather than runtime
14-
- **Domain-specific abstractions**: Model hardware concepts using appropriate Swift types
11+
- Replace magic numbers with strongly-typed values
12+
- Express hardware states with meaningful names
13+
- Catch errors at compile time rather than runtime
14+
- Model hardware concepts using appropriate Swift types
1515

16-
This article guides you through creating custom types that conform to ``MMIO/BitFieldProjectable``, enabling you to represent hardware bit fields in a way that's both safer and more expressive.
16+
This article walks through creating custom types that conform to ``MMIO/BitFieldProjectable``.
1717

1818
### Understanding the BitFieldProjectable protocol
1919

@@ -41,7 +41,7 @@ static var bitWidth: Int { get }
4141

4242
This property defines how many bits your type occupies in the hardware register. This value must **exactly match** the width of the physical bit field as defined in your register macro (e.g., `bits: 4..<6` is 2 bits wide).
4343

44-
A mismatch between your type's `bitWidth` and the actual bit field width will cause a runtime trap when accessing the register. This is a safety feature that ensures your type accurately represents the hardware's capabilities.
44+
A mismatch between your type's `bitWidth` and the actual bit field width causes a runtime trap when accessing the register. This safety feature ensures your type accurately represents the hardware's capabilities.
4545

4646
#### Converting from storage
4747

@@ -69,7 +69,7 @@ This method converts your type back to a raw integer value for writing to the ha
6969
2. Places these bits at the correct position in the register value
7070
3. Writes the complete register value to hardware
7171

72-
The returned value must only use bits up to `Self.bitWidth` or will cause a runtime trap when written. The method should accurately represent your type's state in the bit pattern expected by the hardware
72+
The returned value must only use bits up to `Self.bitWidth` or will cause a runtime trap when written. Make sure it accurately represents your type's state in the bit pattern the hardware expects.
7373

7474
### Projecting an enum
7575

@@ -201,7 +201,7 @@ Finally, let's add a factory method that creates values matching our "off" patte
201201

202202
```swift
203203
struct FanSpeed: BitFieldProjectable, RawRepresentable {
204-
// ...
204+
// ...
205205

206206
static func off(rawValue: UInt8 = 0b00) -> Self {
207207
let value = Self(rawValue: rawValue)
@@ -251,7 +251,7 @@ fanControl.modify { view in
251251
}
252252
```
253253

254-
This approach gives us tremendous flexibility. We can represent specific named values for common states, while also handling bit patterns with "don't care" bits. The custom pattern matching allows us to check if a value matches a particular pattern, ignoring bits that don't affect functionality.
254+
This approach gives us great flexibility. We can represent specific named values for common states while also handling bit patterns with "don't care" bits. The custom pattern matching lets us check if a value matches a pattern, ignoring bits that don't affect functionality.
255255

256256
The real power of this technique becomes apparent when working with hardware that has complex bit field semantics. For example, many hardware peripherals use bit patterns where:
257257
- Some bits are reserved and must be zero

Sources/MMIO/Documentation.docc/Advanced-Topics/Safety-Considerations.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,57 @@ Understand Swift MMIO's guarantees and developer responsibilities.
44

55
## Overview
66

7-
Swift MMIO improves the safety and ergonomics of memory-mapped I/O operations compared to traditional low-level programming in languages like C. However, direct hardware interaction inherently involves aspects that require careful attention from you. This article discusses the safety model and performance characteristics of Swift MMIO.
7+
Swift MMIO is safer and more ergonomic than traditional low-level programming in C. However, direct hardware interaction still requires careful attention. This article covers Swift MMIO's safety model and what you're responsible for.
88

99
### Developer responsibilities
1010

11-
While Swift MMIO introduces significant safety improvements, certain aspects remain inherently unsafe due to the nature of direct hardware interaction. You are responsible for:
11+
Swift MMIO provides significant safety improvements, but direct hardware interaction has inherent risks. You're responsible for:
1212

1313
1. **Correct Base Addresses (`unsafeAddress`):**
1414
Providing the correct base memory address for a `RegisterBlock` or ``MMIO/Register`` is critical. An incorrect address can lead to accessing unintended memory, wrong peripherals, system faults, or data corruption. Always verify addresses against official hardware documentation.
1515

1616
2. **Accurate Register Layout Definitions:**
17-
Definitions for ``MMIO/Register(bitWidth:)`` types (total `bitWidth`, field offsets, widths, access types via macros like ``MMIO/ReadWrite(bits:as:)``) must precisely match hardware documentation. Mismatched layouts can cause misinterpretation of status, incorrect control signals, or unintended modification of adjacent fields/registers. Use tools like `SVD2Swift` with vendor/community-vetted SVD files where possible to reduce manual errors.
17+
``MMIO/Register(bitWidth:)`` definitions (total `bitWidth`, field offsets, widths, access types via macros like ``MMIO/ReadWrite(bits:as:)``) must match hardware documentation exactly. Mismatched layouts cause misinterpreted status, incorrect control signals, or unintended modification of adjacent fields/registers. Use `SVD2Swift` with vendor/community-vetted SVD files when possible to reduce manual errors.
1818

1919
3. **Understanding Hardware Side Effects:**
20-
MMIO register access can have side effects (e.g., read-to-clear flags, initiating time-consuming operations, state-dependent access permissions, timing requirements). Ignoring these can lead to missed events, incorrect state transitions, or hardware errors. Consult datasheets for side effects and manage them in your logic (e.g., polling, interrupt handlers, delays). Swift MMIO ensures volatile memory access but not the logical consequences of that access.
20+
MMIO register access can have side effects (read-to-clear flags, triggering operations, state-dependent permissions, timing requirements). Ignoring these leads to missed events, incorrect state transitions, or hardware errors. Consult datasheets for side effects and handle them in your code (polling, interrupt handlers, delays). Swift MMIO ensures volatile memory access but can't guarantee logical correctness.
2121

2222
### Library responsibilities
2323

2424
Swift MMIO offers several safety layers:
2525

2626
1. **Type Safety:**
27-
Type projections (via ``MMIO/BitFieldProjectable``) allow bit fields to be represented by strong Swift types, preventing out-of-range/meaningless integer assignments.
27+
Type projections (via ``MMIO/BitFieldProjectable``) represent bit fields as strong Swift types, preventing out-of-range or meaningless integer assignments.
2828

2929
2. **Compile-Time Boundary Checking for Fields:**
3030
Bit field definitions are checked at compile-time to be within their parent ``MMIO/Register(bitWidth:)``'s `bitWidth`.
3131

3232
3. **Runtime Checks for Field Access:**
33-
Writing a values too large for a field's width triggers a runtime trap, preventing unintentional modification of adjacent bits.
33+
Writing values too large for a field's width triggers a runtime trap, preventing unintentional modification of adjacent bits.
3434

3535
4. **Volatile Access Guarantee:**
3636
All ``MMIO/Register`` operations use volatile memory semantics, preventing incorrect compiler optimizations for MMIO. See <doc:Volatile-Access>.
3737

3838
5. **Reduced Boilerplate and Manual Bit Manipulation:**
39-
Bit field macros automatically generate the necessary code for bit masking and shifting, avoiding common errors found in manual bitwise manipulation.
39+
Bit field macros generate code for bit masking and shifting automatically, avoiding common manual bitwise manipulation errors.
4040

4141
6. **Clear Access Semantics:**
4242
Bit field macros document intended access patterns and influence the generated API (e.g., no setter for projected `ReadOnly` fields in `Write` view).
4343

4444
7. **Prevention of Unintended Read-Modify-Write Cycles:**
45-
- Swift MMIO's API design, particularly the ``MMIO/Register/modify(_:)`` method, helps prevent a common pitfall found in C-style MMIO access. In C, using a `volatile` pointer to a struct with bitfields, code like:
45+
- Swift MMIO's API design, particularly ``MMIO/Register/modify(_:)``, prevents a common pitfall in C-style MMIO access. In C, using a `volatile` pointer to a struct with bitfields like this:
4646
```c
4747
// C example of potential unintended multiple RMWs
4848
volatile MyRegisterType* myReg = (MyRegisterType*)0x40001000;
4949
myReg->fieldA = 1; // Could be one RMW operation
5050
myReg->fieldB = 2; // Could be another RMW operation
5151
```
52-
can inadvertently result in two distinct read-modify-write sequences on the hardware register. This happens because each assignment to a bitfield member is a separate load of the entire register, modification of the relevant bits, and a store of the entire register. Such multiple RMWs can cause glitches if hardware requires fields to be updated simultaneously or can lead to incorrect behavior if the register state changes between the operations.
52+
can inadvertently trigger two distinct read-modify-write sequences on the hardware register. Each bitfield assignment is a separate load of the entire register, modification of the relevant bits, and store back. Multiple RMWs cause glitches if hardware requires simultaneous field updates or lead to incorrect behavior if register state changes between operations.
5353
- Swift MMIO's `modify` operation avoids this. When you write:
5454
```swift
5555
myRegister.modify { view in
5656
view.fieldA = valueA
5757
view.fieldB = valueB
5858
}
5959
```
60-
Swift MMIO performs a single volatile read of the entire register before the closure, allows modifications to an in-memory representation within the closure, and then performs a single volatile write of the combined result after the closure. This ensures that `fieldA` and `fieldB` (and any other fields modified within the closure) are updated in the hardware as part of one coherent register write.
60+
Swift MMIO performs a single volatile read before the closure, allows modifications to an in-cpu-memory representation inside the closure, then performs a single volatile write after. This ensures `fieldA` and `fieldB` (and all other register fields) are updated in hardware as one coherent register write.

Sources/MMIO/Documentation.docc/Advanced-Topics/Testing-With-Interposers.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ Testing code that interacts with hardware registers presents unique challenges.
1010
- Specialized debugging equipment
1111
- Complex setup procedures to create specific hardware states
1212

13-
These requirements make unit testing hardware-dependent code slow, expensive, and difficult to automate. Yet verifying that your code correctly interacts with hardware registers is crucial for reliable embedded systems.
13+
These requirements make unit testing hardware-dependent code slow, expensive, and difficult to automate. Verifying correct register interaction is crucial for reliable embedded systems.
1414

15-
Swift MMIO addresses this challenge with **interposers**. An interposer intercepts memory operations that would normally target hardware registers. Instead of accessing physical memory, operations on ``MMIO/Register`` instances are redirected to methods on your custom interposer object. This redirection allows you to:
15+
Swift MMIO addresses this with **interposers**. An interposer intercepts memory operations that would normally target hardware registers. Instead of accessing physical memory, operations on ``MMIO/Register`` instances are redirected to methods on your custom interposer object. With interposers, you can:
1616

1717
- Test register interaction logic without actual hardware
1818
- Verify sequences of register reads and writes
@@ -30,8 +30,8 @@ To create an interposer, define a class that conforms to the ``MMIO/MMIOInterpos
3030

3131
Start by creating a basic interposer that simulates memory by storing values in a dictionary. This interposer:
3232
1. Maps memory addresses to values
33-
2. Returns stored values when registers are read
34-
3. Updates stored values when registers are written
33+
2. Returns stored values on reads
34+
3. Updates stored values on writes
3535

3636
First, define the class structure and storage:
3737

@@ -41,7 +41,7 @@ import MMIOInterposable // Use this target for interposer-enabled builds
4141
class BasicInterposer: MMIOInterposer {
4242
// Simulated memory storage - maps addresses to values
4343
private var memory: [UInt: UInt64] = [:]
44-
44+
4545
// Protocol methods will be implemented next
4646
}
4747
```
@@ -73,7 +73,7 @@ Then, implement the `store` method. This method is called whenever code writes t
7373
```swift
7474
class BasicInterposer: MMIOInterposer {
7575
// ...
76-
76+
7777
func store<Value: FixedWidthInteger & UnsignedInteger & _RegisterStorage>(
7878
_ value: Value, to pointer: UnsafeMutablePointer<Value>
7979
) {
@@ -121,7 +121,7 @@ Next, create the tracing interposer class:
121121
class TracingInterposer: MMIOInterposer {
122122
// Record of all register accesses
123123
var trace: [MMIOTraceEvent] = []
124-
124+
125125
// Simulated memory storage
126126
private var simulatedMemory: [UInt: UInt64] = [:]
127127
}
@@ -132,16 +132,16 @@ Now, implement the `load` method to record read operations. This method performs
132132
```swift
133133
class TracingInterposer: MMIOInterposer {
134134
// ...
135-
135+
136136
func load<Value: FixedWidthInteger & UnsignedInteger & _RegisterStorage>(
137137
from pointer: UnsafePointer<Value>
138138
) -> Value {
139139
let address = UInt(bitPattern: pointer)
140140
let value = simulatedMemory[address, default: 0]
141-
141+
142142
// Record this read operation in the trace
143143
trace.append(MMIOTraceEvent(type: .load, address: address, value: value))
144-
144+
145145
return Value(value)
146146
}
147147
}
@@ -152,16 +152,16 @@ Finally, implement the `store` method to record write operations. This method al
152152
```swift
153153
class TracingInterposer: MMIOInterposer {
154154
// ...
155-
155+
156156
func store<Value: FixedWidthInteger & UnsignedInteger & _RegisterStorage>(
157157
_ value: Value, to pointer: UnsafeMutablePointer<Value>
158158
) {
159159
let address = UInt(bitPattern: pointer)
160160
let storedValue = UInt64(value)
161-
161+
162162
// Update simulated memory
163163
simulatedMemory[address] = storedValue
164-
164+
165165
// Record this write operation in the trace
166166
trace.append(MMIOTraceEvent(type: .store, address: address, value: storedValue))
167167
}
@@ -194,10 +194,10 @@ import Testing
194194
struct ControlRegister {
195195
@ReadWrite(bits: 0..<1, as: Bool.self)
196196
var enable: ENABLE
197-
197+
198198
@ReadWrite(bits: 1..<3)
199199
var mode: MODE
200-
200+
201201
@ReadWrite(bits: 3..<8)
202202
var prescaler: PRESCALER
203203
}
@@ -229,22 +229,22 @@ Finally, write a test that verifies the function works correctly. The test:
229229
- Then, it should write the updated value with (binary 00100001 = decimal 33):
230230
- enable=true
231231
- mode=2
232-
- prescaler=4
232+
- prescaler=4
233233

234234
```swift
235235
struct ControlRegisterTests {
236236
@Test func testUpdateWhenDisabled() throws {
237237
let interposer = TracingInterposer() // 1
238-
238+
239239
let control = ControlRegister(unsafeAddress: 0x40000000, interposer: interposer) // 2
240-
240+
241241
updateControlRegister(control, newMode: 2) // 3
242-
242+
243243
let expectedTrace = [
244244
MMIOTraceEvent(type: .load, address: 0x40000000, value: 0), // 4
245245
MMIOTraceEvent(type: .store, address: 0x40000000, value: 33) // 4
246246
]
247-
247+
248248
#expect(interposer.trace == expectedTrace) // 4
249249
}
250250
}

0 commit comments

Comments
 (0)