Skip to content

Commit

Permalink
Better swift support + examples
Browse files Browse the repository at this point in the history
Signed-off-by: paulober <44974737+paulober@users.noreply.github.com>
  • Loading branch information
paulober committed Dec 5, 2024
1 parent aa5dcfe commit 4ab880a
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 25 deletions.
17 changes: 17 additions & 0 deletions scripts/PicoSDK.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// This file contains a swift wrapper for some Pico SDK C style stuff
// Upercase library names can be used to only include helpers for available APIs

#if PICO_STDLIB
public struct PicoGPIO {
/// Enum for GPIO direction, mirroring the C enum
public enum Direction: UInt8 {
case input = 0
case output = 1
}

/// Swift-friendly wrapper for gpio_set_dir
public static func setDirection(pin: UInt32, direction: Direction) {
gpio_set_dir(pin, direction.rawValue != 0)
}
}
#endif
56 changes: 56 additions & 0 deletions scripts/bridge.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,57 @@
#pragma once

#ifndef PICO_DEFAULT_LED_PIN
#define PICO_DEFAULT_LED_PIN 6
#endif

#ifdef _HARDWARE_STRUCTS_SPI_H
#ifdef spi0
spi_inst_t* get_spi0(void) {
return spi0;
}
#endif

#ifdef spi1
spi_inst_t* get_spi1(void) {
return spi1;
}
#endif
#endif

#ifdef _HARDWARE_STRUCTS_I2C_H
#ifdef i2c0
i2c_inst_t* get_i2c0(void) {
return i2c0;
}
#endif

#ifdef i2c1
i2c_inst_t* get_i2c1(void) {
return i2c1;
}
#endif
#endif

#ifdef _HARDWARE_STRUCTS_PIO_H
#ifdef pio0
pio_hw_t* get_pio0(void) {
return pio0;
}
#endif

#ifdef pio1
pio_hw_t* get_pio1(void) {
return pio1;
}
#endif

#ifdef pio2
pio_hw_t* get_pio2(void) {
return pio2;
}
#endif
#endif

/**
* @file bridge.h
* @brief Simplifies the usage of specific SDK features in Swift by providing wrapper functions.
Expand All @@ -11,6 +63,7 @@

#ifdef _HARDWARE_STRUCTS_UART_H // Ensure that UART hardware structs are included before compiling

#ifdef uart0
/**
* @brief Retrieves the instance for UART0.
*
Expand All @@ -22,7 +75,9 @@
uart_inst_t* get_uart0(void) {
return uart0;
}
#endif

#ifdef uart1
/**
* @brief Retrieves the instance for UART1.
*
Expand All @@ -34,5 +89,6 @@ uart_inst_t* get_uart0(void) {
uart_inst_t* get_uart1(void) {
return uart1;
}
#endif

#endif
93 changes: 69 additions & 24 deletions scripts/pico_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ def GDB_NAME():
"// SPI Constants",
"// We are going to use SPI 0, and allocate it to the following GPIO pins",
"// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
'let SPI_PORT = "spi0"',
"let SPI_PORT = get_spi0()",
"let PIN_MISO: UInt32 = 16",
"let PIN_CS: UInt32 = 17",
"let PIN_SCK: UInt32 = 18",
Expand All @@ -446,8 +446,8 @@ def GDB_NAME():
"gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI)",
"",
"// Chip select is active-low, so we'll initialise it to a driven-high state",
"gpio_set_dir(PIN_CS, GPIO_OUT)",
"gpio_put(PIN_CS, 1)",
"PicoGPIO.setDirection(pin: PIN_CS, direction: .output) // Use swift wrapper",
"gpio_put(PIN_CS, true)",
"// For more examples of SPI use see https://github.com/raspberrypi/pico-examples/tree/master/spi",
),
],
Expand All @@ -456,7 +456,7 @@ def GDB_NAME():
"// I2C defines",
"// This example will use I2C0 on GPIO8 (SDA) and GPIO9 (SCL) running at 400KHz.",
"// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
'let I2C_PORT = "i2c0"',
"let I2C_PORT = get_i2c0()",
"let I2C_SDA: UInt32 = 8",
"let I2C_SCL: UInt32 = 9",
),
Expand All @@ -479,24 +479,24 @@ def GDB_NAME():
),
(
"// Get a free channel, panic() if there are none",
"let chan = dma_claim_unused_channel(true)",
"let chan = UInt32(dma_claim_unused_channel(true))",
"",
"// 8 bit transfers. Both read and write address increment after each",
"// transfer (each pointing to a location in src or dst respectively).",
"// No DREQ is selected, so the DMA transfers as fast as it can.",
"",
"let c = dma_channel_get_default_config(chan)",
"var c = dma_channel_get_default_config(chan)",
"channel_config_set_transfer_data_size(&c, DMA_SIZE_8)",
"channel_config_set_read_increment(&c, true)",
"channel_config_set_write_increment(&c, true)",
"",
"dma_channel_configure(",
" chan, // Channel to be configured",
" &c, // The configuration we just created",
" dst, // The initial write address",
" src, // The initial read address",
" count_of(src), // Number of transfers; in this case each is 1 byte.",
" true // Start immediately.",
" chan, // Channel to be configured",
" &c, // The configuration we just created",
" &dst, // The initial write address",
" src, // The initial read address",
" UInt32(src.utf8.count), // Number of transfers; in this case each is 1 byte.",
" true // Start immediately.",
")",
"",
"// We could choose to go and do something else whilst the DMA is doing its",
Expand Down Expand Up @@ -540,7 +540,7 @@ def GDB_NAME():
"guard let pio = get_pio0() else {",
' fatalError("PIO0 not available")',
"}",
"let offset = pio_add_program(pio, &blink_program)",
"let offset = pio_add_program(pio, [blink_program])",
'print("Loaded program at \\(offset)")',
"",
"blink_pin_forever(pio, sm: 0, offset: UInt32(offset), pin: UInt32(PICO_DEFAULT_LED_PIN), freq: 3)",
Expand Down Expand Up @@ -588,7 +588,7 @@ def GDB_NAME():
),
(
"// Timer example code - This example fires off the callback after 2000ms",
"add_alarm_in_ms(2000, alarm_callback, NULL, false)",
"add_alarm_in_ms(2000, alarm_callback, nil, false)",
"// For more examples of timer use see https://github.com/raspberrypi/pico-examples/tree/master/timer",
),
],
Expand Down Expand Up @@ -999,6 +999,8 @@ def GenerateMain(folder, projectName, features, cpp, wantEntryProjName, swift):
o = f'#include "{features_list[feat][H_FILE]}"\n'
if swift and bridging_file:
bridging_file.write(o)
if feat == "pio":
bridging_file.write('#include "blink.pio.h"')
else:
file.write(o)
if feat in stdlib_examples_list:
Expand Down Expand Up @@ -1045,7 +1047,7 @@ def GenerateMain(folder, projectName, features, cpp, wantEntryProjName, swift):
"struct Main {\n"
" \n"
" static func main() {\n"
" stdio_init_all();\n\n"
" stdio_init_all()\n\n"
)
else:
main = "\n\n" "int main()\n" "{\n" " stdio_init_all();\n\n"
Expand Down Expand Up @@ -1225,6 +1227,20 @@ def GenerateCMake(folder, params):

file.write(cmake_header1)
file.write(cmake_header_us)
if params["useSwift"]:
cmake_if_apple = (
"if(APPLE)\n"
" execute_process(COMMAND xcrun -f swiftc OUTPUT_VARIABLE SWIFTC OUTPUT_STRIP_TRAILING_WHITESPACE)\n"
" execute_process(COMMAND dirname ${SWIFTC} OUTPUT_VARIABLE SWIFTC_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)\n"
"elseif(WIN32)\n"
" execute_process(COMMAND where.exe swiftc OUTPUT_VARIABLE SWIFTC OUTPUT_STRIP_TRAILING_WHITESPACE)\n"
' string(REGEX REPLACE "(.*)\\\\\\\\[^\\\\\\\\]*$" "\\\\1" SWIFTC_DIR "${SWIFTC}")\n'
"else()\n"
" execute_process(COMMAND which swiftc OUTPUT_VARIABLE SWIFTC OUTPUT_STRIP_TRAILING_WHITESPACE)\n"
" execute_process(COMMAND dirname ${SWIFTC} OUTPUT_VARIABLE SWIFTC_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)\n"
"endif()\n"
)
file.write(cmake_if_apple)
file.write(cmake_header2)

if params["exceptions"]:
Expand All @@ -1236,13 +1252,6 @@ def GenerateCMake(folder, params):
file.write(cmake_header3)

if params["useSwift"]:
cmake_if_apple = (
"if(APPLE)\n"
" execute_process(COMMAND xcrun -f swiftc OUTPUT_VARIABLE SWIFTC OUTPUT_STRIP_TRAILING_WHITESPACE)\n"
"else()\n"
" execute_process(COMMAND which swiftc OUTPUT_VARIABLE SWIFTC OUTPUT_STRIP_TRAILING_WHITESPACE)\n"
"endif()\n"
)
cmake_swift_target = (
'set(SWIFT_TARGET "armv6m-none-none-eabi") # RP2040\n\n'
'if(PICO_PLATFORM STREQUAL "rp2350-arm-s")\n'
Expand All @@ -1257,8 +1266,12 @@ def GenerateCMake(folder, params):
' set(SWIFT_TARGET "riscv32-none-none-eabi")\n'
' list(APPEND CLANG_ARCH_ABI_FLAGS "-Xcc" "-march=rv32imac_zicsr_zifencei_zba_zbb_zbs_zbkb" "-Xcc" "-mabi=ilp32")\n'
"endif()\n"
'set(SWIFT_EMBEDDED_UNICODE_LIB "${SWIFTC_DIR}/../lib/swift/embedded/${SWIFT_TARGET}/libswiftUnicodeDataTables.a")\n\n'
"# Link the Swift Unicode Data Tables library\n"
"if(NOT EXISTS ${SWIFT_EMBEDDED_UNICODE_LIB})\n"
' message(FATAL_ERROR "Required Swift library not found: ${SWIFT_EMBEDDED_LIB}")\n'
"endif()\n\n"
)
file.write(cmake_if_apple)
file.write(cmake_swift_target)

# add the preprocessor defines for overall configuration
Expand All @@ -1285,6 +1298,12 @@ def GenerateCMake(folder, params):
file.write("# Add the standard library to the build\n")
file.write(f"target_link_libraries({projectName}\n")
file.write(" " + STANDARD_LIBRARIES)
if params["features"]:
for feat in params["features"]:
if feat in features_list:
file.write(" " + features_list[feat][LIB_NAME] + "\n")
if feat in picow_options_list:
file.write(" " + picow_options_list[feat][LIB_NAME] + "\n")
file.write(")\n\n")

main_file_name = f"{entry_point_file_name}.swift"
Expand Down Expand Up @@ -1321,6 +1340,22 @@ def GenerateCMake(folder, params):
"endfunction()\n\n"
f"gather_compile_definitions_recursive({projectName})\n"
"get_property(COMPILE_DEFINITIONS GLOBAL PROPERTY compilerdefs_list)\n\n"
"# ==== BEGIN Generate compile definitions from linked libraries\n"
"# Gather libraries linked to project\n"
f"get_target_property({projectName}_LIBRARIES {projectName} INTERFACE_LINK_LIBRARIES)\n"
f"if(NOT {projectName}_LIBRARIES)\n"
f' set({projectName}_LIBRARIES "") # Default to an empty list if none found\n'
"endif()\n\n"
'set(SWIFTC_LIB_DEFINITIONS "")\n'
f"foreach(LIBRARY IN LISTS {projectName}_LIBRARIES)\n"
" # Convert it to uppercase\n"
" string(TOUPPER ${LIBRARY} LIB_NAME_UPPER)\n"
' list(APPEND SWIFTC_LIB_DEFINITIONS "-D" "${LIB_NAME_UPPER}")\n'
"endforeach()\n\n"
"# Convert the list to a string for the Swift command\n"
'string(REPLACE " " ";" SWIFTC_DEFINITIONS_STR "${SWIFTC_LIB_DEFINITIONS}")\n'
'message(STATUS "SWIFTC_LIB_DEFINITIONS: ${SWIFTC_DEFINITIONS_STR}")\n'
"# ==== END Generate compile definitions from linked libraries\n\n"
"# Parse compiler definitions into a format that swiftc can understand\n"
"list(REMOVE_DUPLICATES COMPILE_DEFINITIONS)\n"
'list(PREPEND COMPILE_DEFINITIONS "")\n'
Expand All @@ -1332,13 +1367,16 @@ def GenerateCMake(folder, params):
" ${SWIFTC}\n"
" -target ${SWIFT_TARGET} -Xcc -mfloat-abi=soft -Xcc -fshort-enums\n"
" -Xfrontend -function-sections -enable-experimental-feature Embedded -wmo -parse-as-library\n"
" ${SWIFTC_DEFINITIONS_STR}\n"
f" $$\( echo '$<TARGET_PROPERTY:{projectName},INCLUDE_DIRECTORIES>' | tr '\;' '\\\\n' | sed -e 's/\\\\\(.*\\\\\)/-Xcc -I\\\\1/g' \)\n"
" $$\( echo '${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}' | tr ' ' '\\\\n' | sed -e 's/\\\\\(.*\\\\\)/-Xcc -I\\\\1/g' \)\n"
" -import-bridging-header ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h\n"
" ${USERHOME}/.pico-sdk/sdk/PicoSDK.swift\n"
f" ${{CMAKE_CURRENT_LIST_DIR}}/{main_file_name}\n"
" -c -o ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o\n"
" DEPENDS\n"
" ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h\n"
" ${USERHOME}/.pico-sdk/sdk/PicoSDK.swift\n"
f" ${{CMAKE_CURRENT_LIST_DIR}}/{main_file_name}\n"
")\n"
)
Expand Down Expand Up @@ -1401,6 +1439,13 @@ def GenerateCMake(folder, params):
file.write(" " + STANDARD_LIBRARIES)
file.write(")\n\n")
else:
file.write("# Link Swift Unicode data tables to the build\n")
file.write(f"target_link_options({projectName} PRIVATE\n")
file.write(
" -Wl,--whole-archive ${SWIFT_EMBEDDED_UNICODE_LIB} -Wl,--no-whole-archive\n"
)
file.write(")\n\n")

file.write("# Add the swift artifact to the build\n")
file.write(f"target_link_libraries({projectName}\n")
file.write(" ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o\n")
Expand All @@ -1420,7 +1465,7 @@ def GenerateCMake(folder, params):
file.write(f"add_dependencies({projectName} {projectName}-swiftcode)\n")

# Selected libraries/features
if params["features"]:
if params["features"] and not params["useSwift"]:
file.write("# Add any user requested libraries\n")
file.write(f"target_link_libraries({projectName} \n")
for feat in params["features"]:
Expand Down
8 changes: 8 additions & 0 deletions src/utils/download.mts
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,14 @@ export async function downloadAndInstallSDK(

const targetDirectory = buildSDKPath(version);

// Copy swift wrapper
const sdkRoot = dirname(targetDirectory);
await mkdir(sdkRoot, { recursive: true });
copyFileSync(
joinPosix(getScriptsRoot(), "PicoSDK.swift"),
joinPosix(sdkRoot, "PicoSDK.swift")
);

// Check if the SDK is already installed
if (
existsSync(targetDirectory) &&
Expand Down
2 changes: 1 addition & 1 deletion src/webview/newProjectPanel.mts
Original file line number Diff line number Diff line change
Expand Up @@ -1811,7 +1811,7 @@ export class NewProjectPanel {
</div>
</div>`
: `<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">
Warning: Project Import Wizard may not work for all projects, and will often require manual correction after the import
Warning: Project Import Wizard may not work for all projects, and will often require manual correction after the import. Swift Projects aren't supported at the moment.
</h3>`
}
<div class="mt-6 mb-4">
Expand Down

0 comments on commit 4ab880a

Please sign in to comment.