Board HALs with Swift MMIO

This is a question but also maybe a discussion point. For my swift-stm32c011-examples project, I noticed that although I was able to bring in register definitions with SVD2Swift, they weren't too useful without creating helpers, like for example for configuring and using GPIOs:

extension GPIO {
    public enum Mode: UInt32 {
        case input = 0x0
        case output = 0x1
        case alternateFunction = 0x2
        case analog = 0x3
    }

    public enum OutputType: UInt32 {
        case pushPull = 0x0
        case openDrain = 0x1
    }

    public enum OutputSpeed: UInt32 {
        case low = 0x0
        case medium = 0x1
        case high = 0x2
        case max = 0x3
    }

    public enum Pull: UInt32 {
        case `none` = 0x0
        case up = 0x1
        case down = 0x2
    }

    public struct Configuration {
        public var mode: Mode
        public var outputType: OutputType
        public var outputSpeed: OutputSpeed
        public var pull: Pull
        public var alternateFunction: UInt32

        public init(
            mode: Mode,
            outputType: OutputType,
            outputSpeed: OutputSpeed,
            pull: Pull,
            alternateFunction: UInt32 = 0
        ) {
            self.mode = mode
            self.outputType = outputType
            self.outputSpeed = outputSpeed
            self.pull = pull
            self.alternateFunction = alternateFunction
        }
    }

    public func get(pin: Int) -> UInt32 {
        return self.idr.read().raw.storage.get(
            mask: 0b1,
            offset: UInt8(pin)
        )
    }

    public func set(pin: Int, value: UInt32) {
        self.odr.modify { rw in
            rw.raw.storage.set(
                value: value,
                mask: 0b1,
                offset: pin
            )
        }
    }

    public func configure(pin: Int, as configuration: Configuration) {
        self.moder.modify { rw in
            rw.raw.storage.set(
                value: configuration.mode.rawValue,
                mask: 0b11,
                offset: pin * 2
            )
        }

        // Comprised of 16 x 1 bit fields.
        self.otyper.modify { rw in
            rw.raw.storage.set(
                value: configuration.outputType.rawValue,
                mask: 0b1,
                offset: pin
            )
        }

        // Comprised of 16 x 2 bit fields.
        self.ospeedr.modify { rw in
            rw.raw.storage.set(
                value: configuration.outputSpeed.rawValue,
                mask: 0b11,
                offset: pin * 2
            )
        }

        // Comprised of 16 x 2 bit fields.
        self.pupdr.modify { rw in
            rw.raw.storage.set(
                value: configuration.pull.rawValue,
                mask: 0b11,
                offset: pin * 2
            )
        }

        // Comprised of 16 x 4 bit fields, split across 2 registers.
        if pin < 8 {
            self.afrl.modify { rw in
                rw.raw.storage.set(
                    value: configuration.alternateFunction,
                    mask: 0b1111,
                    offset: pin * 4
                )
            }
        } else {
            self.afrh.modify { rw in
                rw.raw.storage.set(
                    value: configuration.alternateFunction,
                    mask: 0b1111,
                    offset: (pin - 8) * 4
                )
            }
        }
    }
}

So my question is, has there been any thought or discussion towards perhaps standardizing a HAL-library for Swift that can be used for interacting with different things on embedded, that could take different lower-level register definitions and abstract that away for embedded applications?

Kind of like the STM32 CubeC0 HAL...but for Swift?

For now I've been building a small library with a bunch of these helpers for the STM32C011 as I start using the different peripherals, but I was curious if this was a consideration for a future effort.

Absolutely been on my radar, but I've been swamped and haven't made any sharable progress.

I initially went down the route of defining a GPIOProtocol with board specific implementations so higher level code could write against some GPIOProtocol. The module vending GPIOProtocol also provided OutputType, PinSpeed, etc enums, as you've done here, but without rawValues.

The device specific library was responsible for mapping from the generic GPIO API to its specific value for that enum case.

One big open question was how to handle hardware devices not supporting the gamut of possible GPIO options. Ideally I wanted to represent this as a static assert, but I wasn't sure how practical that would be (even if we had the feature).

1 Like

Sweet. I think having some protocols would be nice, to be able to define some common APIs that can be used in application code. Where did you envision these protocols living? Inside of swift-mmio, or perhaps a different library?

I think that if some protocols for different peripherals are defined (like ADC, I2C, GPIO, SPI, USART, and so forth) then it could be quite nice to be creating libraries that could conform to those protocols. And like you mention, have the library assert if it cannot support a certain feature.

I'd be interested to know if you make any progress in this area, and I'd love to contribute as well to help define this.

1 Like

I'm not sure swift-mmio is the best long term home for them, but I'd be happy to incubate the ideas there until we out grow it :)

2 Likes

HAL packages for Swift are IMHO desperately missing currently in the Embedded Swift ecosystem. Both a target specific HAL, or a more generalized multi-target HAL layer, are things worth building and publishing.

2 Likes

Well the ecosystem is incredibly new after all. But, maybe a package like swift-embedded-hal could be nice? Add common definitions with protocols like @rauhul suggests, and keep adding peripherals..

I think once we have something we're generally happy with I'm happy to push for this, but I think we need something more tangible before doing so. Theres a nontrivial maintenance burden for additional repos.

1 Like

Sounds good to me. So, put together stuff in some branch like in swift-mmio, then move it elsewhere once it's built out more.