Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert SVDEnumerations into BitFieldProjections #119

Merged
merged 3 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,10 @@ extension FixedWidthInteger {
// result would have overflowed anyway.
return (self + (powerOfTwo &- 1)) & (0 &- powerOfTwo)
}

public func roundedUpToPowerOfTwo() -> Self {
if self == 0 { return 0 }
let shifts = self.bitWidth &- self.leadingZeroBitCount
return self.nonzeroBitCount == 1 ? self : 1 &<< shifts
}
}
8 changes: 8 additions & 0 deletions Sources/SVD/Models/SVDEnumerationCaseData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ public enum SVDEnumerationCaseData {
extension SVDEnumerationCaseData: Sendable {}

extension SVDEnumerationCaseData: XMLElementInitializable {
static func value(_ value: UInt64, mask: UInt64) -> Self {
.value(.init(value: .init(value: value, mask: mask)))
}

static func isDefault() -> Self {
.isDefault(.init(isDefault: true))
}

init(_ element: XMLElement) throws {
if let value = try? SVDEnumerationCaseDataValue(element) {
self = .value(value)
Expand Down
4 changes: 0 additions & 4 deletions Sources/SVD/Models/SVDEnumerationCaseDataValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ public struct SVDEnumerationCaseDataValue {
public var value: SVDEnumerationCaseDataValueValue
}

extension SVDEnumerationCaseDataValue {
public func bitPatterns() -> [UInt64] { [] }
}

extension SVDEnumerationCaseDataValue: Decodable {}

extension SVDEnumerationCaseDataValue: Encodable {}
Expand Down
21 changes: 21 additions & 0 deletions Sources/SVD/Models/SVDEnumerationCaseDataValueValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ public struct SVDEnumerationCaseDataValueValue {
public var mask: UInt64
}

extension SVDEnumerationCaseDataValueValue {
public func description(bitWidth: Int) -> String {
guard bitWidth > 0 else { return "" }
var description = "0b"

var needle: UInt64 = 1 << (bitWidth - 1)
while needle > 0 {
if self.mask & needle == 0 {
description.append("x")
} else if self.value & needle > 0 {
description += "1"
} else {
description += "0"
}
needle >>= 1
}

return description
}
}

extension SVDEnumerationCaseDataValueValue: Decodable {}

extension SVDEnumerationCaseDataValueValue: Encodable {}
Expand Down
183 changes: 179 additions & 4 deletions Sources/SVD2Swift/Extensions/SVD+Export.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,20 @@ extension SVDDevice {
defer { exportQueue.formIndex(after: &currentIndex) }

let currentContext = exportQueue[currentIndex]

// This is a hack to move the generated BitFieldProjectable one scope
// higher, from out of the field type generated by the BitField macro
// and into the register type generated by SVD2Swift.
var scopeContext = currentContext
let childIsEnumeratedValue =
scopeContext.types.count == 1
&& currentContext.types[0] is SVDEnumeration
if childIsEnumeratedValue {
scopeContext.swiftParentTypeNames.removeLast()
}

let scope =
if let name = currentContext.swiftParentTypeFullName {
if let name = scopeContext.swiftParentTypeFullName {
"extension \(name)"
} else {
""
Expand Down Expand Up @@ -466,6 +478,7 @@ extension SVDRegister: SVDExportable {
print(
"""
warning: skipped exporting \(context.swiftTypeName): unknown register size

""")
return []
}
Expand Down Expand Up @@ -523,7 +536,17 @@ extension SVDField: SVDExportable {
self.description?.coalescingConsecutiveSpaces() ?? swiftTypeName
}

func childTypes() -> [any SVDExportable] { [] }
func childTypes() -> [any SVDExportable] {
if let enumeration = self.enumeratedValues {
let usage = enumeration.usage ?? .readWrite
// FIXME: support read / insert only projections
// FIXME: support derivedFrom
if usage == .readWrite {
return [enumeration]
}
}
return []
}

func exportType(
outputWriter: inout OutputWriter,
Expand Down Expand Up @@ -551,6 +574,23 @@ extension SVDField: SVDExportable {
case nil: "Reserved"
}

var enumeration: SVDEnumeration?
if let enumeratedValues = self.enumeratedValues {
let usage = enumeratedValues.usage ?? .readWrite
// FIXME: support read / insert only projections
// FIXME: support derivedFrom
if usage == .readWrite {
enumeration = enumeratedValues
}
}

func _projection() -> String {
guard let enumeration = enumeration else { return "" }
let name = enumeration.swiftTypeName(
context: context.asParentContext().childContext(for: enumeration))
return ", as: \(name).self"
}

let range = self.bitRange.range
if let dimensionElement = self.dimensionElement {
let count = dimensionElement.dim
Expand All @@ -564,17 +604,152 @@ extension SVDField: SVDExportable {
outputWriter.insert(
"""
\(comment: context.swiftDescription)
@\(macro)(bits: \(range.lowerBound + bitOffset)..<\(range.upperBound + bitOffset))
@\(macro)(bits: \(range.lowerBound + bitOffset)..<\(range.upperBound + bitOffset)\(_projection()))
\(options.accessLevel)var \(identifier: "\(context.swiftInstanceName)\(index)"): \(context.swiftTypeName)\(index)
""")
}
} else {
outputWriter.insert(
"""
\(comment: context.swiftDescription)
@\(macro)(bits: \(range.lowerBound)..<\(range.upperBound))
@\(macro)(bits: \(range.lowerBound)..<\(range.upperBound)\(_projection()))
\(options.accessLevel)var \(identifier: context.swiftInstanceName): \(context.swiftTypeName)
""")
}
}
}

extension SVDEnumeration: SVDExportable {
var registerProperties: SVDRegisterProperties { .none }

func swiftTypeName(context: ExportContext) -> String {
var name =
self.name?.removingUnsafeCharacters()
?? context.swiftParentTypeNames.last.map { "\($0)Values" }
?? "Unknown"
if name == context.swiftParentTypeNames.last {
name.append("Values")
}
return name
}

func swiftDescription(swiftTypeName: String) -> String { "" }

func exportType(
outputWriter: inout OutputWriter,
options: ExportOptions,
context: ExportContext
) -> [any SVDExportable] {

let bitWidth = context.registerProperties.size ?? 0
let rawValueWidth = max(8, bitWidth.roundedUpToPowerOfTwo())
let rawValueType = "UInt\(rawValueWidth)"

let requiresPattern = self.enumeratedValue.contains {
guard case .value(let value) = $0.data else { return false }
return value.value.mask != .max
}

let typeScope = """
\(options.accessLevel)struct \(context.swiftTypeName): BitFieldProjectable, RawRepresentable
"""
outputWriter.scope(typeScope) { outputWriter in
outputWriter.insert(
"""
\(options.accessLevel)static let bitWidth = \(bitWidth)
""")

if requiresPattern {
let patternScope = "\(options.accessLevel)struct Pattern"
outputWriter.scope(patternScope) { outputWriter in
outputWriter.insert(
"""
var rawValue: UInt8
var mask: UInt8
""")
}

let patternMatchScope = """
\(options.accessLevel)static func ~= (pattern: Pattern, value: Self) -> Bool
"""
outputWriter.scope(patternMatchScope) { outputWriter in
outputWriter.insert(
"""
(value.rawValue & pattern.mask) == pattern.rawValue
""")
}
}

for enumeratedValue in self.enumeratedValue {
switch enumeratedValue.data {
case .value(let value):
var swiftName =
enumeratedValue.name
?? value.value.description(bitWidth: Int(bitWidth))
let swiftDescription = enumeratedValue.description ?? swiftName
swiftName = swiftName.removingUnsafeCharacters()
if swiftName.first?.isLetter == false {
swiftName.insert("_", at: swiftName.startIndex)
}

let mask = value.value.mask
let value = value.value.value

switch mask {
case .max:
// All bits specified
outputWriter.insert(
"""
\(comment: swiftDescription)
\(options.accessLevel)static let \(swiftName) = Self(rawValue: \(hex: value))
""")

default:
outputWriter.insert(
"""
\(comment: swiftDescription)
\(options.accessLevel)static let \(swiftName) = Pattern(rawValue: \(hex: value), mask: \(hex: mask, bits: bitWidth))
""")

let factoryScope = """
\(comment: swiftDescription)
\(options.accessLevel)static func \(swiftName)(_ rawValue: \(rawValueType) = \(swiftName).value) -> Self
"""
outputWriter.scope(factoryScope) { outputWriter in
outputWriter.insert(
"""
let value = Self(rawValue: rawValue)
precondition(\(swiftName) ~= \(value), "Invalid bits set in rawValue")
return value
""")
}
}

case .isDefault:
print("warning: ignored for now...")
}
}

outputWriter.insert(
"""
\(options.accessLevel)var rawValue: \(rawValueType)
""")

let initScope = """
@inlinable @inline(__always)
\(options.accessLevel)init(rawValue: Self.RawValue)
"""
outputWriter.scope(initScope) { outputWriter in
outputWriter.insert("self.rawValue = rawValue")
}
}

return []
}

func exportAccessor(
outputWriter: inout OutputWriter,
options: ExportOptions,
context: ExportContext
) {}
}
Loading