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: Sources/MMIO/Documentation.docc/Advanced-Topics/Custom-BitFieldProjectable.md
+10-10Lines changed: 10 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,14 +6,14 @@ Define projections to map hardware bit fields to meaningful Swift types.
6
6
7
7
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.
8
8
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:
10
10
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
15
15
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``.
17
17
18
18
### Understanding the BitFieldProjectable protocol
19
19
@@ -41,7 +41,7 @@ static var bitWidth: Int { get }
41
41
42
42
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).
43
43
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.
45
45
46
46
#### Converting from storage
47
47
@@ -69,7 +69,7 @@ This method converts your type back to a raw integer value for writing to the ha
69
69
2. Places these bits at the correct position in the register value
70
70
3. Writes the complete register value to hardware
71
71
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.
73
73
74
74
### Projecting an enum
75
75
@@ -201,7 +201,7 @@ Finally, let's add a factory method that creates values matching our "off" patte
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.
255
255
256
256
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:
Copy file name to clipboardExpand all lines: Sources/MMIO/Documentation.docc/Advanced-Topics/Safety-Considerations.md
+10-10Lines changed: 10 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,57 +4,57 @@ Understand Swift MMIO's guarantees and developer responsibilities.
4
4
5
5
## Overview
6
6
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.
8
8
9
9
### Developer responsibilities
10
10
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:
12
12
13
13
1.**Correct Base Addresses (`unsafeAddress`):**
14
14
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.
15
15
16
16
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.
18
18
19
19
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.
21
21
22
22
### Library responsibilities
23
23
24
24
Swift MMIO offers several safety layers:
25
25
26
26
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.
28
28
29
29
2.**Compile-Time Boundary Checking for Fields:**
30
30
Bit field definitions are checked at compile-time to be within their parent ``MMIO/Register(bitWidth:)``'s `bitWidth`.
31
31
32
32
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.
34
34
35
35
4.**Volatile Access Guarantee:**
36
36
All ``MMIO/Register`` operations use volatile memory semantics, preventing incorrect compiler optimizations for MMIO. See <doc:Volatile-Access>.
37
37
38
38
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.
40
40
41
41
6.**Clear Access Semantics:**
42
42
Bit field macros document intended access patterns and influence the generated API (e.g., no setter for projected `ReadOnly` fields in `Write` view).
43
43
44
44
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 bitfieldslike this:
46
46
```c
47
47
// C example of potential unintended multiple RMWs
myReg->fieldB = 2; // Could be another RMW operation
51
51
```
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 ifthe 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 ifregister state changes between operations.
53
53
- Swift MMIO's `modify` operation avoids this. When you write:
54
54
```swift
55
55
myRegister.modify { view in
56
56
view.fieldA = valueA
57
57
view.fieldB = valueB
58
58
}
59
59
```
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.
Copy file name to clipboardExpand all lines: Sources/MMIO/Documentation.docc/Advanced-Topics/Testing-With-Interposers.md
+20-20Lines changed: 20 additions & 20 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,9 +10,9 @@ Testing code that interacts with hardware registers presents unique challenges.
10
10
- Specialized debugging equipment
11
11
- Complex setup procedures to create specific hardware states
12
12
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.
14
14
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:
16
16
17
17
- Test register interaction logic without actual hardware
18
18
- Verify sequences of register reads and writes
@@ -30,8 +30,8 @@ To create an interposer, define a class that conforms to the ``MMIO/MMIOInterpos
30
30
31
31
Start by creating a basic interposer that simulates memory by storing values in a dictionary. This interposer:
32
32
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
35
35
36
36
First, define the class structure and storage:
37
37
@@ -41,7 +41,7 @@ import MMIOInterposable // Use this target for interposer-enabled builds
41
41
classBasicInterposer: MMIOInterposer {
42
42
// Simulated memory storage - maps addresses to values
43
43
privatevar memory: [UInt: UInt64] = [:]
44
-
44
+
45
45
// Protocol methods will be implemented next
46
46
}
47
47
```
@@ -73,7 +73,7 @@ Then, implement the `store` method. This method is called whenever code writes t
0 commit comments