Skip to content

Commit aa5dcfe

Browse files
committed
Update swift examples and memory alignment support
Signed-off-by: paulober <[email protected]>
1 parent adcaee7 commit aa5dcfe

File tree

4 files changed

+178
-48
lines changed

4 files changed

+178
-48
lines changed

scripts/bridge.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <errno.h>
2+
#include <malloc.h>
3+
4+
/**
5+
* @brief Allocates aligned memory in accordance with POSIX standards.
6+
*
7+
* The `posix_memalign` function allocates a block of memory with the specified alignment
8+
* and size. The allocated memory is stored in the location pointed to by `memptr`. The
9+
* alignment must be a power of two and a multiple of `sizeof(void *)`. This function is
10+
* typically used for ensuring memory alignment for hardware or performance requirements.
11+
*
12+
* @param[out] memptr A pointer to the memory location where the aligned memory will be stored.
13+
* This parameter must not be NULL.
14+
* @param[in] alignment The alignment boundary in bytes. Must be a power of two and a multiple
15+
* of `sizeof(void *)`.
16+
* @param[in] size The size of the memory block to allocate in bytes.
17+
*
18+
* @return int Returns 0 on success. On failure, returns:
19+
* - `EINVAL` if the alignment is invalid (not a power of two or not a multiple of `sizeof(void *)`).
20+
* - `ENOMEM` if memory allocation fails.
21+
*
22+
* @note The caller is responsible for freeing the allocated memory using `free()` when it is no longer needed.
23+
*/
24+
int posix_memalign(void **memptr, size_t alignment, size_t size) {
25+
// Validate alignment requirements
26+
if ((alignment % sizeof(void *) != 0) || (alignment & (alignment - 1)) != 0) {
27+
return EINVAL; // Invalid alignment
28+
}
29+
30+
// Use memalign to allocate memory
31+
void *ptr = memalign(alignment, size);
32+
if (ptr == NULL) {
33+
return ENOMEM; // Memory allocation failure
34+
}
35+
36+
*memptr = ptr; // Set the memory pointer
37+
return 0; // Success
38+
}

scripts/bridge.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#pragma once
2+
3+
/**
4+
* @file bridge.h
5+
* @brief Simplifies the usage of specific SDK features in Swift by providing wrapper functions.
6+
*
7+
* This header acts as a bridge between C and Swift, exposing certain features of the Pico SDK
8+
* in a simplified manner. For example it includes helper functions for accessing predefined
9+
* UART instances (`uart0` and `uart1`), making them easily callable from Swift.
10+
*/
11+
12+
#ifdef _HARDWARE_STRUCTS_UART_H // Ensure that UART hardware structs are included before compiling
13+
14+
/**
15+
* @brief Retrieves the instance for UART0.
16+
*
17+
* This function provides access to the pre-defined `uart0` instance,
18+
* allowing straightforward interaction with the UART hardware from Swift.
19+
*
20+
* @return Pointer to the `uart0` instance.
21+
*/
22+
uart_inst_t* get_uart0(void) {
23+
return uart0;
24+
}
25+
26+
/**
27+
* @brief Retrieves the instance for UART1.
28+
*
29+
* This function provides access to the pre-defined `uart1` instance,
30+
* allowing straightforward interaction with the UART hardware from Swift.
31+
*
32+
* @return Pointer to the `uart1` instance.
33+
*/
34+
uart_inst_t* get_uart1(void) {
35+
return uart1;
36+
}
37+
38+
#endif

scripts/pico_project.py

Lines changed: 80 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -394,13 +394,13 @@ def GDB_NAME():
394394
(
395395
"// UART defines",
396396
"// By default the stdout UART is `uart0`, so we will use the second one",
397-
'let UART_ID = "uart1"',
398-
"let BAUD_RATE = 115200",
397+
"let UART_ID = get_uart1()",
398+
"let BAUD_RATE: UInt32 = 115200",
399399
"",
400400
"// Use pins 4 and 5 for UART1",
401401
"// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
402-
"let UART_TX_PIN = 4",
403-
"let UART_RX_PIN = 5",
402+
"let UART_TX_PIN: UInt32 = 4",
403+
"let UART_RX_PIN: UInt32 = 5",
404404
),
405405
(
406406
"// Set up our UART",
@@ -432,10 +432,10 @@ def GDB_NAME():
432432
"// We are going to use SPI 0, and allocate it to the following GPIO pins",
433433
"// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
434434
'let SPI_PORT = "spi0"',
435-
"let PIN_MISO = 16",
436-
"let PIN_CS = 17",
437-
"let PIN_SCK = 18",
438-
"let PIN_MOSI = 19",
435+
"let PIN_MISO: UInt32 = 16",
436+
"let PIN_CS: UInt32 = 17",
437+
"let PIN_SCK: UInt32 = 18",
438+
"let PIN_MOSI: UInt32 = 19",
439439
),
440440
(
441441
"// SPI initialisation. This example will use SPI at 1MHz.",
@@ -457,8 +457,8 @@ def GDB_NAME():
457457
"// This example will use I2C0 on GPIO8 (SDA) and GPIO9 (SCL) running at 400KHz.",
458458
"// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
459459
'let I2C_PORT = "i2c0"',
460-
"let I2C_SDA = 8",
461-
"let I2C_SCL = 9",
460+
"let I2C_SDA: UInt32 = 8",
461+
"let I2C_SCL: UInt32 = 9",
462462
),
463463
(
464464
"// I2C Initialisation. Using it at 400Khz.",
@@ -511,37 +511,47 @@ def GDB_NAME():
511511
],
512512
"pio": [
513513
(
514-
'#include "blink.pio.h"',
515-
"static func blink_pin_forever(pio: PIO, sm: uint, offset: uint, pin: uint, freq: uint) {",
514+
"func blink_pin_forever(_ pio: PIO, sm: UInt32, offset: UInt32, pin: UInt32, freq: UInt32) {",
516515
" blink_program_init(pio, sm, offset, pin)",
517516
" pio_sm_set_enabled(pio, sm, true)",
518517
"",
519-
' printf("Blinking pin %d at %d Hz\\n", pin, freq)',
518+
' print("Blinking pin \\(pin) at \\(freq) Hz")',
520519
"",
521520
" // PIO counter program takes 3 more cycles in total than we pass as",
522521
" // input (wait for n + 1; mov; jmp)",
523-
" pio.txf[sm] = (125000000 / (2 * freq)) - 3",
522+
" let value = (125000000 / (2 * freq)) - 3",
523+
" switch sm {",
524+
" case 0:",
525+
" pio.pointee.txf.0 = value",
526+
" case 1:",
527+
" pio.pointee.txf.1 = value",
528+
" case 2:",
529+
" pio.pointee.txf.2 = value",
530+
" case 3:",
531+
" pio.pointee.txf.3 = value",
532+
" default:",
533+
" // There are 4 state machines available",
534+
' fatalError("Invalid state machine index")',
535+
" }",
524536
"}",
525537
),
526538
(
527539
"// PIO Blinking example",
528-
"let pio = pio0",
540+
"guard let pio = get_pio0() else {",
541+
' fatalError("PIO0 not available")',
542+
"}",
529543
"let offset = pio_add_program(pio, &blink_program)",
530-
'printf("Loaded program at %d\\n", offset)',
544+
'print("Loaded program at \\(offset)")',
531545
"",
532-
"#ifdef PICO_DEFAULT_LED_PIN",
533-
"blink_pin_forever(pio, 0, offset, PICO_DEFAULT_LED_PIN, 3)",
534-
"#else",
535-
"blink_pin_forever(pio, 0, offset, 6, 3)",
536-
"#endif",
546+
"blink_pin_forever(pio, sm: 0, offset: UInt32(offset), pin: UInt32(PICO_DEFAULT_LED_PIN), freq: 3)",
537547
"// For more pio examples see https://github.com/raspberrypi/pico-examples/tree/master/pio",
538548
),
539549
],
540550
"clocks": [
541551
(),
542552
(
543-
'printf("System Clock Frequency is %d Hz\\n", clock_get_hz(clk_sys))',
544-
'printf("USB Clock Frequency is %d Hz\\n", clock_get_hz(clk_usb))',
553+
'print("System Clock Frequency is \\(clock_get_hz(clk_sys)) Hz")',
554+
'print("USB Clock Frequency is \\(clock_get_hz(clk_usb)) Hz")',
545555
"// For more examples of clocks use see https://github.com/raspberrypi/pico-examples/tree/master/clocks",
546556
),
547557
],
@@ -610,12 +620,12 @@ def GDB_NAME():
610620
"let divisor = -321",
611621
"// This is the recommended signed fast divider for general use.",
612622
"let result = hw_divider_divmod_s32(dividend, divisor)",
613-
'printf("%d/%d = %d remainder %d\\n", dividend, divisor, to_quotient_s32(result), to_remainder_s32(result))',
623+
'print("\\(dividend)/\\(divisor) = \\(to_quotient_s32(result)) remainder \\(to_remainder_s32(result))")',
614624
"// This is the recommended unsigned fast divider for general use.",
615625
"let udividend = 123456",
616626
"let udivisor = 321",
617627
"let uresult = hw_divider_divmod_u32(udividend, udivisor)",
618-
'printf("%d/%d = %d remainder %d\\n", udividend, udivisor, to_quotient_u32(uresult), to_remainder_u32(uresult))',
628+
'printf("\\(udividend)/\\(udivisor) = \\(to_quotient_u32(uresult)) remainder \\(to_remainder_u32(uresult))")',
619629
"// See https://github.com/raspberrypi/pico-examples/tree/master/divider for more complex use",
620630
),
621631
],
@@ -631,15 +641,15 @@ def GDB_NAME():
631641
(
632642
"// Enable wifi station",
633643
"cyw43_arch_enable_sta_mode()\n",
634-
'printf("Connecting to Wi-Fi...\\n")',
644+
'print("Connecting to Wi-Fi...")',
635645
'if cyw43_arch_wifi_connect_timeout_ms("Your Wi-Fi SSID", "Your Wi-Fi Password", CYW43_AUTH_WPA2_AES_PSK, 30000) {',
636-
' printf("failed to connect.\\n")',
646+
' print("failed to connect.")',
637647
" return 1",
638648
"} else {",
639-
' printf("Connected.\\n")',
649+
' print("Connected.")',
640650
" // Read the ip address in a human readable way",
641651
" let ip_address = (uint8_t*)&(cyw43_state.netif[0].ip_addr.addr)",
642-
' printf("IP address %d.%d.%d.%d\\n", ip_address[0], ip_address[1], ip_address[2], ip_address[3])',
652+
' print("IP address \\(ip_address[0]).\\(ip_address[1]).\\(ip_address[2]).\\(ip_address[3])")',
643653
"}",
644654
),
645655
],
@@ -703,6 +713,9 @@ def codeSdkPath(sdkVersion):
703713
return f"${{userHome}}{relativeSDKPath(sdkVersion)}"
704714

705715

716+
CODE_BASIC_TOOLCHAIN_PATH = "${userHome}/.pico-sdk/toolchain"
717+
718+
706719
def codeOpenOCDPath(openocdVersion):
707720
return f"${{userHome}}{relativeOpenOCDPath(openocdVersion)}"
708721

@@ -984,23 +997,23 @@ def GenerateMain(folder, projectName, features, cpp, wantEntryProjName, swift):
984997
if len(features_list[feat][H_FILE]) == 0:
985998
continue
986999
o = f'#include "{features_list[feat][H_FILE]}"\n'
987-
if swift:
1000+
if swift and bridging_file:
9881001
bridging_file.write(o)
9891002
else:
9901003
file.write(o)
9911004
if feat in stdlib_examples_list:
9921005
if len(stdlib_examples_list[feat][H_FILE]) == 0:
9931006
continue
9941007
o = f'#include "{stdlib_examples_list[feat][H_FILE]}"\n'
995-
if swift:
1008+
if swift and bridging_file:
9961009
bridging_file.write(o)
9971010
else:
9981011
file.write(o)
9991012
if feat in picow_options_list:
10001013
if len(picow_options_list[feat][H_FILE]) == 0:
10011014
continue
10021015
o = f'#include "{picow_options_list[feat][H_FILE]}"\n'
1003-
if swift:
1016+
if swift and bridging_file:
10041017
bridging_file.write(o)
10051018
else:
10061019
file.write(o)
@@ -1017,7 +1030,7 @@ def GenerateMain(folder, projectName, features, cpp, wantEntryProjName, swift):
10171030
for feat in features:
10181031
if feat in frags:
10191032
for s in frags[feat][DEFINES]:
1020-
if swift and s.startswith("#include"):
1033+
if swift and bridging_file and s.startswith("#include"):
10211034
bridging_file.write(s)
10221035
bridging_file.write("\n")
10231036
file.write(s)
@@ -1092,7 +1105,10 @@ def GenerateMain(folder, projectName, features, cpp, wantEntryProjName, swift):
10921105

10931106
file.write(main)
10941107

1095-
bridging_file.close()
1108+
if bridging_file:
1109+
bridging_file.write("// always include last\n")
1110+
bridging_file.write("#include <bridge.h>\n\n")
1111+
bridging_file.close()
10961112
file.close()
10971113

10981114

@@ -1259,9 +1275,17 @@ def GenerateCMake(folder, params):
12591275
entry_point_file_name = projectName if params["wantEntryProjName"] else "main"
12601276

12611277
if params["wantCPP"]:
1262-
file.write(f"add_executable({projectName} {entry_point_file_name}.cpp )\n\n")
1278+
file.write(f"add_executable({projectName} {entry_point_file_name}.cpp)\n\n")
12631279
elif params["useSwift"]:
1264-
file.write(f"add_executable({projectName})\n\n")
1280+
file.write(
1281+
f"add_executable({projectName} ${{USERHOME}}/.pico-sdk/toolchain/bridge.c)\n\n"
1282+
)
1283+
1284+
# Standard libraries
1285+
file.write("# Add the standard library to the build\n")
1286+
file.write(f"target_link_libraries({projectName}\n")
1287+
file.write(" " + STANDARD_LIBRARIES)
1288+
file.write(")\n\n")
12651289

12661290
main_file_name = f"{entry_point_file_name}.swift"
12671291
cmake_custom_swift_command = (
@@ -1273,7 +1297,7 @@ def GenerateCMake(folder, params):
12731297
" get_property(visited_targets GLOBAL PROPERTY visited_targets)\n\n"
12741298
" # make sure we don't visit the same target twice\n"
12751299
" # and that we don't visit the special generator expressions\n"
1276-
' if (${target} MATCHES "\\$<" OR ${target} MATCHES "::@" OR ${target} IN_LIST visited_targets)\n'
1300+
' if (${target} MATCHES "\\\\$<" OR ${target} MATCHES "::@" OR ${target} IN_LIST visited_targets)\n'
12771301
" return()\n"
12781302
" endif()\n\n"
12791303
" # Append the target to visited_targets\n"
@@ -1311,11 +1335,11 @@ def GenerateCMake(folder, params):
13111335
f" $$\( echo '$<TARGET_PROPERTY:{projectName},INCLUDE_DIRECTORIES>' | tr '\;' '\\\\n' | sed -e 's/\\\\\(.*\\\\\)/-Xcc -I\\\\1/g' \)\n"
13121336
" $$\( echo '${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}' | tr ' ' '\\\\n' | sed -e 's/\\\\\(.*\\\\\)/-Xcc -I\\\\1/g' \)\n"
13131337
" -import-bridging-header ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h\n"
1314-
f" ${{CMAKE_CURRENT_LIST_DIR}}/{entry_point_file_name}.swift\n"
1338+
f" ${{CMAKE_CURRENT_LIST_DIR}}/{main_file_name}\n"
13151339
" -c -o ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o\n"
13161340
" DEPENDS\n"
13171341
" ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h\n"
1318-
f" ${{CMAKE_CURRENT_LIST_DIR}}/{entry_point_file_name}.swift\n"
1342+
f" ${{CMAKE_CURRENT_LIST_DIR}}/{main_file_name}\n"
13191343
")\n"
13201344
)
13211345
file.write(cmake_custom_swift_command)
@@ -1370,18 +1394,26 @@ def GenerateCMake(folder, params):
13701394
file.write(f'WIFI_PASSWORD="${WIFI_PASSWORD}"')
13711395
file.write(")\n\n")
13721396

1373-
# Standard libraries
1374-
file.write("# Add the standard library to the build\n")
1375-
file.write(f"target_link_libraries({projectName}\n")
1376-
file.write(" " + STANDARD_LIBRARIES)
1377-
if params["useSwift"]:
1378-
file.write(" ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o\n")
1379-
file.write(")\n\n")
1397+
if not params["useSwift"]:
1398+
# Standard libraries
1399+
file.write("# Add the standard library to the build\n")
1400+
file.write(f"target_link_libraries({projectName}\n")
1401+
file.write(" " + STANDARD_LIBRARIES)
1402+
file.write(")\n\n")
1403+
else:
1404+
file.write("# Add the swift artifact to the build\n")
1405+
file.write(f"target_link_libraries({projectName}\n")
1406+
file.write(" ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o\n")
1407+
file.write(")\n\n")
13801408

13811409
# Standard include directories
13821410
file.write("# Add the standard include files to the build\n")
13831411
file.write(f"target_include_directories({projectName} PRIVATE\n")
13841412
file.write(" ${CMAKE_CURRENT_LIST_DIR}\n")
1413+
if params["useSwift"]:
1414+
# bridge.h includes serveral helper functions
1415+
# for swift-c-pico-sdk interop
1416+
file.write(" ${USERHOME}/.pico-sdk/toolchain\n")
13851417
file.write(")\n\n")
13861418

13871419
if params["useSwift"]:
@@ -1543,7 +1575,8 @@ def generateProjectFiles(
15431575
"name": "Pico",
15441576
"includePath": [
15451577
"${{workspaceFolder}}/**",
1546-
"{codeSdkPath(sdkVersion)}/**"
1578+
"{codeSdkPath(sdkVersion)}/**"{f""",
1579+
"{CODE_BASIC_TOOLCHAIN_PATH}\"""" if useSwift else ""}
15471580
],
15481581
"forcedInclude": [
15491582
"{codeSdkPath(sdkVersion)}/src/common/{base_headers_folder_name}/include/pico.h",

0 commit comments

Comments
 (0)