From 0e12c69cc11f7f8c6dc46ab525145a2830a99a60 Mon Sep 17 00:00:00 2001 From: Rauhul Varma Date: Tue, 20 Aug 2024 12:47:13 -0700 Subject: [PATCH 1/2] Add support to svd2swift for exporting clusters Updates the svd2swift code-generation tool to emit clusters as recursively nested RegisterBlocks. Refactors the svd2swift internals to simplify the tool. A future refactor which separates creating a type scope from generating the type's accessors should probably be made. Fixes #85 --- Sources/SVD2Swift/Extensions/SVD+Export.swift | 306 ++++++++++++------ .../SVD2SwiftTests+Clusters.swift | 169 ++++++++++ 2 files changed, 384 insertions(+), 91 deletions(-) create mode 100644 Tests/SVD2SwiftTests/SVD2SwiftTests+Clusters.swift diff --git a/Sources/SVD2Swift/Extensions/SVD+Export.swift b/Sources/SVD2Swift/Extensions/SVD+Export.swift index 5d21f520..be2b6ec2 100644 --- a/Sources/SVD2Swift/Extensions/SVD+Export.swift +++ b/Sources/SVD2Swift/Extensions/SVD+Export.swift @@ -36,7 +36,6 @@ struct ExportContext { } extension SVDDevice { - // FIXME: pass register props throughout this func export( with options: ExportOptions, to output: inout Output @@ -55,8 +54,6 @@ extension SVDDevice { fileprivate func export( context: inout ExportContext ) throws { - let registerProperties = self.registerProperties - var outputPeripherals = [SVDPeripheral]() if context.selectedPeripherals.isEmpty { outputPeripherals = self.peripherals.peripheral @@ -108,17 +105,123 @@ extension SVDDevice { for peripheral in outputPeripherals { context.outputWriter.append(fileHeader) - peripheral.exportType( - context: &context, - registerProperties: registerProperties, - deviceName: deviceName) + + var parentTypes = [String]() + if context.namespaceUnderDevice { + parentTypes.append(deviceName) + } + + var exportQueue = [([any SVDExportable], [String], SVDRegisterProperties)]() + exportQueue.append(([peripheral], parentTypes, self.registerProperties)) + + // Track indices instead of popping front to avoid O(N) pop. + var currentIndex = exportQueue.startIndex + while currentIndex < exportQueue.endIndex { + defer { exportQueue.formIndex(after: ¤tIndex) } + if currentIndex != exportQueue.startIndex { + context.outputWriter.append("\n") + } + let (elements, parentTypes, registerProperties) = exportQueue[currentIndex] + if !parentTypes.isEmpty { + let parentTypeFullName = parentTypes.joined(separator: ".") + context.outputWriter.append("extension \(parentTypeFullName) {\n") + context.outputWriter.indent() + } + + for (index, element) in elements.enumerated() { + let (exports, registerProperties) = element.exportType( + context: &context, + registerProperties: registerProperties) + if !exports.isEmpty { + exportQueue.append(( + exports, + parentTypes + [element.swiftName], + registerProperties)) + } + if index < elements.count - 1 { + context.outputWriter.append("\n") + } + } + + if !parentTypes.isEmpty { + context.outputWriter.outdent() + context.outputWriter.append("}\n") + } + } try context.outputWriter.writeOutput(to: "\(peripheral.swiftName).swift") } } } -extension SVDPeripheral { - fileprivate func exportAccessor( +protocol SVDExportable { + var swiftName: String { get } + + func exportType( + context: inout ExportContext, + registerProperties: SVDRegisterProperties + ) -> ([any SVDExportable], SVDRegisterProperties) + + func exportAccessor( + context: inout ExportContext, + registerProperties: consuming SVDRegisterProperties) +} + +extension SVDPeripheral: SVDExportable { + func exportType( + context: inout ExportContext, + registerProperties: SVDRegisterProperties + ) -> ([any SVDExportable], SVDRegisterProperties) { + let typeName = self.swiftName + let registerProperties = self.registerProperties.merging(registerProperties) + + var exports = [any SVDExportable]() + if let derivedFrom = self.derivedFrom { + // FIXME: Handle only exporting B where B deriveFrom A + context.outputWriter.append("\(context.accessLevel)typealias \(typeName) = \(derivedFrom)\n") + } else { + context.outputWriter.append( + """ + \(comment: self.swiftDescription) + @RegisterBlock + \(context.accessLevel)struct \(typeName) { + + """) + context.outputWriter.indent() + if let registersAndClusters = self.registers { + let registers = registersAndClusters.register + let clusters = registersAndClusters.cluster + + for (index, register) in registers.enumerated() { + register.exportAccessor( + context: &context, + registerProperties: registerProperties) + exports.append(register) + if index < registers.count - 1 { + context.outputWriter.append("\n") + } + } + + if !registers.isEmpty, !clusters.isEmpty { + context.outputWriter.append("\n") + } + + for (index, cluster) in clusters.enumerated() { + cluster.exportAccessor( + context: &context, + registerProperties: registerProperties) + exports.append(cluster) + if index < clusters.count - 1 { + context.outputWriter.append("\n") + } + } + } + context.outputWriter.outdent() + context.outputWriter.append("}\n") + } + return (exports, registerProperties) + } + + func exportAccessor( context: inout ExportContext, registerProperties: consuming SVDRegisterProperties ) { @@ -150,6 +253,9 @@ extension SVDPeripheral { \(context.accessLevel)\(accessorModifier)let \(identifier: "\(instanceName)\(index)") = \(typeName)(unsafeAddress: \(hex: self.baseAddress + addressOffset)) """) + if index < count - 1 { + context.outputWriter.append("\n") + } } } else { context.outputWriter.append( @@ -160,87 +266,106 @@ extension SVDPeripheral { """) } } +} - fileprivate func exportType( +extension SVDCluster: SVDExportable { + func exportAccessor( context: inout ExportContext, - registerProperties: SVDRegisterProperties, - deviceName: String + registerProperties: SVDRegisterProperties ) { let typeName = self.swiftName + let instanceName = typeName.lowercased() let registerProperties = self.registerProperties.merging(registerProperties) - if context.namespaceUnderDevice { - context.outputWriter.append("extension \(deviceName) {\n") - context.outputWriter.indent() - } + if let count = self.dimensionElement.dim { + // FIXME: properties.size may be incorrect here. + let stride = self.dimensionElement.dimIncrement ?? registerProperties.size + guard let stride = stride else { + // FIXME: warning diagnostic + print("warning: skipped exporting \(instanceName): unknown stride") + return + } - if let derivedFrom = self.derivedFrom { - // FIXME: Handle only exporting B where B deriveFrom A - context.outputWriter.append("\(context.accessLevel)typealias \(typeName) = \(derivedFrom)\n") - if context.namespaceUnderDevice { - context.outputWriter.outdent() - context.outputWriter.append("}\n") + for index in 0.. ([any SVDExportable], SVDRegisterProperties) { + let registerProperties = self.registerProperties.merging(registerProperties) + var exports = [any SVDExportable]() + + if let derivedFrom = self.derivedFrom { + context.outputWriter.append("\(context.accessLevel)typealias \(self.swiftName) = \(derivedFrom)\n") + } else { - context.outputWriter.append( + context.outputWriter.append( """ \(comment: self.swiftDescription) @RegisterBlock - \(context.accessLevel)struct \(typeName) { - + \(context.accessLevel)struct \(self.swiftName) { + """) - context.outputWriter.indent() - if let registers = self.registers { - // FIXME: add cluster export - let registers = registers.register - for (index, register) in registers.enumerated() { - register.exportAccessor( - context: &context, - registerProperties: registerProperties) - if index < registers.count - 1 { - context.outputWriter.append("\n") + context.outputWriter.indent() + if let registers = self.register { + for (index, register) in registers.enumerated() { + register.exportAccessor( + context: &context, + registerProperties: registerProperties) + exports.append(register) + if index < registers.count - 1 { + context.outputWriter.append("\n") + } } } - } - context.outputWriter.outdent() - context.outputWriter.append("}\n") - if context.namespaceUnderDevice { - context.outputWriter.outdent() - context.outputWriter.append("}\n") - } - context.outputWriter.append("\n") - - let name = - if context.namespaceUnderDevice { - "\(deviceName).\(typeName)" - } else { - typeName + if !(self.register ?? []).isEmpty, !(self.cluster ?? []).isEmpty { + context.outputWriter.append("\n") } - context.outputWriter.append("extension \(name) {\n") - context.outputWriter.indent() - if let registers = self.registers { - // FIXME: add cluster export - let registers = registers.register - for (index, register) in registers.enumerated() { - register.exportType( - context: &context, - registerProperties: registerProperties) - if index < registers.count - 1 { - context.outputWriter.append("\n") + + if let clusters = self.cluster { + for (index, cluster) in clusters.enumerated() { + cluster.exportAccessor( + context: &context, + registerProperties: registerProperties) + exports.append(cluster) + if index < clusters.count - 1 { + context.outputWriter.append("\n") + } } } + context.outputWriter.outdent() + context.outputWriter.append("}\n") } - context.outputWriter.outdent() - context.outputWriter.append("}\n") + return (exports, registerProperties) } } -extension SVDRegister { - fileprivate func exportAccessor( +extension SVDRegister: SVDExportable { + func exportAccessor( context: inout ExportContext, registerProperties: SVDRegisterProperties ) { @@ -275,39 +400,38 @@ extension SVDRegister { } } - fileprivate func exportType( + func exportType( context: inout ExportContext, - registerProperties: SVDRegisterProperties - ) { - let typeName = self.swiftName - + registerProperties: SVD.SVDRegisterProperties + ) -> ([any SVDExportable], SVDRegisterProperties) { let registerProperties = self.registerProperties.merging(registerProperties) - guard let size = registerProperties.size else { - // FIXME: warning diagnostic - print("warning: skipped exporting \(typeName): unknown register size") - return - } - context.outputWriter.append( + if let size = registerProperties.size { + context.outputWriter.append( """ \(comment: self.swiftDescription) @Register(bitWidth: \(size)) - \(context.accessLevel)struct \(typeName) { - + \(context.accessLevel)struct \(self.swiftName) { + """) - context.outputWriter.indent() - let fields = self.fields?.field ?? [] - for (index, field) in fields.enumerated() { - field.exportAccessor( - context: &context, - register: self, - registerProperties: registerProperties) - if index < fields.count - 1 { - context.outputWriter.append("\n") + context.outputWriter.indent() + let fields = self.fields?.field ?? [] + for (index, field) in fields.enumerated() { + field.exportAccessor( + context: &context, + register: self, + registerProperties: registerProperties) + if index < fields.count - 1 { + context.outputWriter.append("\n") + } } + context.outputWriter.outdent() + context.outputWriter.append("}\n") + } else { + // FIXME: warning diagnostic + print("warning: skipped exporting \(self.swiftName): unknown register size") } - context.outputWriter.outdent() - context.outputWriter.append("}\n") + return ([], registerProperties) } } @@ -358,8 +482,8 @@ extension SVDField { if let count = self.dimensionElement.dim { let stride = self.dimensionElement.dimIncrement ?? UInt64(range.count) // FIXME: array fields - // Instead of splatting out N copies of the field we should have some way to - // describe an array like RegisterArray + // Instead of splatting out N copies of the field we should have some way + // to describe an array like RegisterArray for index in 0.. + + /// An example cluster level 1 + @RegisterBlock(offset: 0x100) + var examplecluster1: ExampleCluster1 + + /// Another example cluster level 1 + @RegisterBlock(offset: 0x200) + var examplecluster1_1: ExampleCluster1_1 + } + + extension ExamplePeripheral { + /// An example register + @Register(bitWidth: 32) + struct ExampleRegister { + /// ExampleField + @ReadWrite(bits: 4..<11) + var examplefield: ExampleField + } + + /// An example cluster level 1 + @RegisterBlock + struct ExampleCluster1 { + /// An example register in cluster 1 + @RegisterBlock(offset: 0x10) + var examplecluster1register: Register + + /// An example cluster level 2 + @RegisterBlock(offset: 0x30) + var examplecluster2: ExampleCluster2 + } + + /// Another example cluster level 1 + @RegisterBlock + struct ExampleCluster1_1 { + } + } + + extension ExamplePeripheral.ExampleCluster1 { + /// An example register in cluster 1 + @Register(bitWidth: 32) + struct ExampleCluster1Register { + } + + /// An example cluster level 2 + @RegisterBlock + struct ExampleCluster2 { + /// An example register in cluster 2 + @RegisterBlock(offset: 0x0) + var examplecluster2register: Register + } + } + + extension ExamplePeripheral.ExampleCluster1.ExampleCluster2 { + /// An example register in cluster 2 + @Register(bitWidth: 32) + struct ExampleCluster2Register { + } + } + + """, + ]) + } +} From 8095c453e34f95a1d44a7a0060c3f738d6447648 Mon Sep 17 00:00:00 2001 From: Rauhul Varma Date: Wed, 21 Aug 2024 10:24:54 -0700 Subject: [PATCH 2/2] fix formatting --- Sources/SVD2Swift/Extensions/SVD+Export.swift | 36 ++++++++++--------- .../SVD2SwiftTests+Clusters.swift | 8 ++--- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Sources/SVD2Swift/Extensions/SVD+Export.swift b/Sources/SVD2Swift/Extensions/SVD+Export.swift index be2b6ec2..dfc022a5 100644 --- a/Sources/SVD2Swift/Extensions/SVD+Export.swift +++ b/Sources/SVD2Swift/Extensions/SVD+Export.swift @@ -133,10 +133,12 @@ extension SVDDevice { context: &context, registerProperties: registerProperties) if !exports.isEmpty { - exportQueue.append(( - exports, - parentTypes + [element.swiftName], - registerProperties)) + exportQueue.append( + ( + exports, + parentTypes + [element.swiftName], + registerProperties + )) } if index < elements.count - 1 { context.outputWriter.append("\n") @@ -184,7 +186,7 @@ extension SVDPeripheral: SVDExportable { \(comment: self.swiftDescription) @RegisterBlock \(context.accessLevel)struct \(typeName) { - + """) context.outputWriter.indent() if let registersAndClusters = self.registers { @@ -323,12 +325,12 @@ extension SVDCluster: SVDExportable { } else { context.outputWriter.append( - """ - \(comment: self.swiftDescription) - @RegisterBlock - \(context.accessLevel)struct \(self.swiftName) { - - """) + """ + \(comment: self.swiftDescription) + @RegisterBlock + \(context.accessLevel)struct \(self.swiftName) { + + """) context.outputWriter.indent() if let registers = self.register { for (index, register) in registers.enumerated() { @@ -408,12 +410,12 @@ extension SVDRegister: SVDExportable { if let size = registerProperties.size { context.outputWriter.append( - """ - \(comment: self.swiftDescription) - @Register(bitWidth: \(size)) - \(context.accessLevel)struct \(self.swiftName) { - - """) + """ + \(comment: self.swiftDescription) + @Register(bitWidth: \(size)) + \(context.accessLevel)struct \(self.swiftName) { + + """) context.outputWriter.indent() let fields = self.fields?.field ?? [] for (index, field) in fields.enumerated() { diff --git a/Tests/SVD2SwiftTests/SVD2SwiftTests+Clusters.swift b/Tests/SVD2SwiftTests/SVD2SwiftTests+Clusters.swift index d65f987d..32b2ea15 100644 --- a/Tests/SVD2SwiftTests/SVD2SwiftTests+Clusters.swift +++ b/Tests/SVD2SwiftTests/SVD2SwiftTests+Clusters.swift @@ -57,7 +57,7 @@ extension SVD2SwiftTests { .init( name: "ExampleCluster1_1", description: "Another example cluster level 1", - addressOffset: 0x200) + addressOffset: 0x200), ], register: [ .init( @@ -129,18 +129,18 @@ extension SVD2SwiftTests { /// An example register in cluster 1 @RegisterBlock(offset: 0x10) var examplecluster1register: Register - + /// An example cluster level 2 @RegisterBlock(offset: 0x30) var examplecluster2: ExampleCluster2 } - + /// Another example cluster level 1 @RegisterBlock struct ExampleCluster1_1 { } } - + extension ExamplePeripheral.ExampleCluster1 { /// An example register in cluster 1 @Register(bitWidth: 32)