Skip to content
Open
Show file tree
Hide file tree
Changes from 17 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 @@ -20,6 +20,7 @@ Embedded Swift is a compilation and language mode that enables development of ba
- <doc:macOSGuide>
- <doc:PicoGuide>
- <doc:STM32BaremetalGuide>
- <doc:LLDBGuide>

### Using Embedded Swift

Expand Down
773 changes: 773 additions & 0 deletions Sources/EmbeddedSwift/Documentation.docc/GuidedExamples/LLDBGuide.md

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions rpi-pico-lldb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# rpi-pico-lldb
The sample code used in the [Try out LLDB debugging on Raspberry Pi Pico](https://docs.swift.org/embedded/documentation/embedded/lldbguide) Guided Example.

## Prerequisites
- Install **Swift with Embedded support**, **LLDB**, and **SVD2LLDB**.
- A **RP2350 / RP2040** board with access to SWD (debug) pins, and an LED. On-board LEDs may be used as long as they are connected directly to GPIO; otherwise, you may use an external LED.
- A **SWD / JTAG debugger**, allowing GDB remote debug protocol connections. If you have another Raspberry Pi Pico or a Raspberry Pi Debug Probe, you can use it for debugging, via OpenOCD.

## Project layout
- **[`start-tutorial/`](start-tutorial)** — Starting point for following along with the guide.
- **[`finished-tutorial/`](finished-tutorial)** — Completed version with the bugs fixed.

## Building
This project compiles for both RP2040 and RP2350. However, you will need to specify your board while compiling:

```shell
$ cd rpi-pico-lldb/start-tutorial; make BOARD=RP2040 // or BOARD=RP2350
```
83 changes: 83 additions & 0 deletions rpi-pico-lldb/finished-tutorial/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift open source project
##
## Copyright (c) 2024 Apple Inc. and the Swift project authors.
## Licensed under Apache License v2.0 with Runtime Library Exception
##
## See https://swift.org/LICENSE.txt for license information
##
##===----------------------------------------------------------------------===##

# Determine file paths
REPOROOT := $(shell git rev-parse --show-toplevel)
TOOLSROOT := $(REPOROOT)/Tools
MACHO2UF2 := $(TOOLSROOT)/macho2uf2.py
MACHO2ELF := $(TOOLSROOT)/macho2elf.py
SWIFT_BUILD := swift build
BUILD_SYSTEM := native

# Board-specific configuration
ifndef BOARD
$(error BOARD is required. Use: make BOARD=RP2040 or make BOARD=RP2350)
endif

BOARD_LC := $(shell echo $(BOARD) | tr A-Z a-z)

ifeq ($(BOARD_LC),rp2040)
TOOLSET := $(TOOLSROOT)/Toolsets/pico.json
ARCH := armv6m
TRAIT := RP2040
PICO_FAMILY := rp2040
else ifeq ($(BOARD_LC),rp2350)
TOOLSET := $(TOOLSROOT)/Toolsets/pico2.json
ARCH := armv7em
TRAIT := RP2350
PICO_FAMILY := rp2350
else
$(error Unknown BOARD '$(BOARD)'. Use BOARD=RP2040 or BOARD=RP2350)
endif

# Setup tools and build flags
TARGET := $(ARCH)-apple-none-macho
SWIFT_BUILD_ARGS := \
--build-system $(BUILD_SYSTEM) \
--configuration debug \
--triple $(TARGET) \
--toolset $(TOOLSET) \
--traits $(TRAIT)
BUILDROOT := $(shell $(SWIFT_BUILD) $(SWIFT_BUILD_ARGS) --show-bin-path)

.PHONY: build
build:
@echo "building..."
$(SWIFT_BUILD) \
$(SWIFT_BUILD_ARGS) \
-Xlinker -map -Xlinker $(BUILDROOT)/Application.mangled.map \
--verbose

@echo "demangling linker map..."
cat $(BUILDROOT)/Application.mangled.map \
| c++filt | swift demangle > $(BUILDROOT)/Application.map

@echo "disassembling..."
otool \
-arch $(ARCH) -v -V -d -t \
$(BUILDROOT)/Application \
| c++filt | swift demangle > $(BUILDROOT)/Application.disassembly

@echo "extracting binary..."
$(MACHO2UF2) \
$(BUILDROOT)/Application \
$(BUILDROOT)/Application.uf2 \
--pico-family $(PICO_FAMILY) \
--base-address 0x20000000 \
--segments '__TEXT,__DATA,__VECTORS,__RESET'



.PHONY: clean
clean:
@echo "cleaning..."
@swift package clean
@rm -rf .build
33 changes: 33 additions & 0 deletions rpi-pico-lldb/finished-tutorial/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions rpi-pico-lldb/finished-tutorial/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// swift-tools-version: 6.1

import PackageDescription

let package = Package(
name: "rpi-pico-lldb",
products: [
.executable(name: "Application", targets: ["Application"])
],
traits: [
"RP2040",
"RP2350"
],
dependencies: [
.package(url: "https://github.com/apple/swift-mmio.git", branch: "main")
],
targets: [
.executableTarget(
name: "Application",
dependencies: [
.target(name: "RP2040", condition: .when(traits: ["RP2040"])),
.target(name: "RP2350", condition: .when(traits: ["RP2350"])),
.product(name: "MMIO", package: "swift-mmio"),
"Support"
]),
.target(
name: "RP2350",
dependencies: [
.product(name: "MMIO", package: "swift-mmio")
],
plugins: [
.plugin(name: "SVD2SwiftPlugin", package: "swift-mmio")
]),
.target(
name: "RP2040",
dependencies: [
.product(name: "MMIO", package: "swift-mmio")
],
plugins: [
.plugin(name: "SVD2SwiftPlugin", package: "swift-mmio")
]),
.target(
name: "Support",
cSettings: [
.define("RP2040", .when(traits: ["RP2040"])),
.define("RP2350", .when(traits: ["RP2350"]))
])
],
swiftLanguageModes: [.v5])
7 changes: 7 additions & 0 deletions rpi-pico-lldb/finished-tutorial/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Finished tutorial
This is the bugs-fixed version sample code.

Bugs fixed:
- In `MemoryI2CDevice.init(I2C1_SCL: UInt32, I2C1_SDA: UInt32, address: UInt32, enableInternalPullUp: Bool)`, `disableI2C()` must be called before calling `configBus()`.According to the RP2350/RP2040 datasheet, setting the `ic_con` and `ic_sar` registers requires the I2C interface to be disabled beforehand.
- In `Application.main()`, an incorrect address of the I2C peripheral was used (`controller.configBus(targetPeripheral: 0x43)`). It was changed to the correct address, `0x42`.
- The on-board LED's pin (`LED_PIN`) was incorrect (originally `100`). This was changed to the correct pin of the Pico's on-board LED (`25`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors.
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

#if RP2040 && RP2350
#error("Enable only one: RP2040 or RP2350")
#endif

#if RP2350
import RP2350
#elseif RP2040
import RP2040
#elseif !(RP2040 || RP2350)
#error("Pick a chip: build with --traits RP2040 or --traits RP2350")
#endif

import MMIO
import Support

// I2C pins
let I2C0_SDA: UInt32 = 16
let I2C0_SCL: UInt32 = 17
let I2C1_SDA: UInt32 = 26
let I2C1_SCL: UInt32 = 27

@inline(__always)
private func delay(_ n: UInt32) {
var i = n
while i > 0 {
nop()
i &-= 1
}
}

@_cdecl("putchar")
public func putchar(_ value: CInt) -> CInt {
nop()
return 0
}

// On-board LED turned on continuously on success
private func ledSuccess() {
ledSet(true)
while true {
nop()
}
}

private func blinkFailForever(_ code: UInt32) -> Never {
let n = max(1, min(code, 20))
while true {
var i: UInt32 = 0
while i < n {
ledSet(true)
delay(600_000)
ledSet(false)
delay(600_000)
i &+= 1
}
delay(4_000_000)
}
}

private func enableInterfaces() {
// Take required peripherals out of reset
//
// RP2350 datasheet section 7.5 Subsystem resets:
// "When reset, components are held in reset at power-up.
// To use the component, software must deassert the reset."
resets.reset.modify { rw in
rw.raw.pads_bank0 = 0
rw.raw.io_bank0 = 0
rw.raw.i2c0 = 0
rw.raw.i2c1 = 0
}

// Wait until reset_done shows they’re out of reset
while resets.reset_done.read().raw.pads_bank0 == 0 {}
while resets.reset_done.read().raw.io_bank0 == 0 {}
while resets.reset_done.read().raw.i2c0 == 0 {}
while resets.reset_done.read().raw.i2c1 == 0 {}

// LED pin init
configureLedPinSIO(LED_PIN)
ledSet(false)

// I2C pins config
// In this config, we use external pull-ups
let useInternalPullUps = false
configureI2CPin(I2C0_SDA, enableInternalPullUp: useInternalPullUps)
configureI2CPin(I2C0_SCL, enableInternalPullUp: useInternalPullUps)
}

@main
struct Application {
static func main() {
enableInterfaces()
let controller = I2CController(I2C0_SCL: I2C0_SCL, I2C0_SDA: I2C0_SDA)
let memory = MemoryI2CDevice(I2C1_SCL: I2C1_SCL, I2C1_SDA: I2C1_SDA)

// We first save a byte in our I2C memory.
// Then, we read the saved byte, to check that everything works.

// Configure I2C target (address of memory)
controller.configBus(targetPeripheral: 0x42)

// Controller sends byte to I2C memory
let txByte: UInt8 = 0xA5
controller.writeByte(txByte)

// Memory I2C peripheral reads & saves byte, incremented
memory.receiveBytesToMemory()

// Controller requests byte from I2C Memory
controller.requestByteFromMemory()

// Memory serves the incremented byte
memory.serveBytesFromMemory()

// Controller receives the byte
let readValue = controller.receiveRequestedBytesFromMemory()

// Validate the transmission
if readValue != nil && readValue! == txByte + 1 {
ledSuccess()
} else {
blinkFailForever(2)
}
}
}
55 changes: 55 additions & 0 deletions rpi-pico-lldb/finished-tutorial/Sources/Application/I2C.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors.
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

#if RP2350
import RP2350
#elseif RP2040
import RP2040
#elseif !(RP2040 || RP2350)
#error("Pick a chip: build with --traits RP2040 or --traits RP2350")
#endif

import Support
import MMIO

// I2C pin mux + pad config
func configureI2CPin(_ pin: UInt32, enableInternalPullUp: Bool) {
pads_bank0.gpio[pin].modify { rw in
rw.raw.od = 0
rw.raw.ie = 1
rw.raw.pue = enableInternalPullUp ? 1 : 0
rw.raw.pde = 0
rw.raw.schmitt = 1
rw.raw.slewfast = 0
}

// Mux to I2C (funcsel 0x3)
io_bank0.gpio[pin].gpio_ctrl.modify { rw in
rw.raw.funcsel = 0x3
}

#if RP2350
// Remove pad isolation
pads_bank0.gpio[pin].modify { rw in
rw.raw.iso = 0
}
#endif
}


// Wait for a condition to become true (blocking; may block forever)
@inline(__always)
func waitForCondition(_ cond: () -> Bool) {
while true {
if cond() { return }
nop()
}
}
Loading