Skip to content

Commit da4bd52

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

File tree

4 files changed

+179
-49
lines changed

4 files changed

+179
-49
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: 81 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -392,13 +392,13 @@ def GDB_NAME():
392392
(
393393
"// UART defines",
394394
"// By default the stdout UART is `uart0`, so we will use the second one",
395-
'let UART_ID = "uart1"',
396-
"let BAUD_RATE = 115200",
395+
"let UART_ID = get_uart1()",
396+
"let BAUD_RATE: UInt32 = 115200",
397397
"",
398398
"// Use pins 4 and 5 for UART1",
399399
"// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
400-
"let UART_TX_PIN = 4",
401-
"let UART_RX_PIN = 5",
400+
"let UART_TX_PIN: UInt32 = 4",
401+
"let UART_RX_PIN: UInt32 = 5",
402402
),
403403
(
404404
"// Set up our UART",
@@ -430,10 +430,10 @@ def GDB_NAME():
430430
"// We are going to use SPI 0, and allocate it to the following GPIO pins",
431431
"// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
432432
'let SPI_PORT = "spi0"',
433-
"let PIN_MISO = 16",
434-
"let PIN_CS = 17",
435-
"let PIN_SCK = 18",
436-
"let PIN_MOSI = 19",
433+
"let PIN_MISO: UInt32 = 16",
434+
"let PIN_CS: UInt32 = 17",
435+
"let PIN_SCK: UInt32 = 18",
436+
"let PIN_MOSI: UInt32 = 19",
437437
),
438438
(
439439
"// SPI initialisation. This example will use SPI at 1MHz.",
@@ -455,8 +455,8 @@ def GDB_NAME():
455455
"// This example will use I2C0 on GPIO8 (SDA) and GPIO9 (SCL) running at 400KHz.",
456456
"// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
457457
'let I2C_PORT = "i2c0"',
458-
"let I2C_SDA = 8",
459-
"let I2C_SCL = 9",
458+
"let I2C_SDA: UInt32 = 8",
459+
"let I2C_SCL: UInt32 = 9",
460460
),
461461
(
462462
"// I2C Initialisation. Using it at 400Khz.",
@@ -509,37 +509,47 @@ def GDB_NAME():
509509
],
510510
"pio": [
511511
(
512-
'#include "blink.pio.h"',
513-
"static func blink_pin_forever(pio: PIO, sm: uint, offset: uint, pin: uint, freq: uint) {",
512+
"func blink_pin_forever(_ pio: PIO, sm: UInt32, offset: UInt32, pin: UInt32, freq: UInt32) {",
514513
" blink_program_init(pio, sm, offset, pin)",
515514
" pio_sm_set_enabled(pio, sm, true)",
516515
"",
517-
' printf("Blinking pin %d at %d Hz\\n", pin, freq)',
516+
' print("Blinking pin \\(pin) at \\(freq) Hz")',
518517
"",
519518
" // PIO counter program takes 3 more cycles in total than we pass as",
520519
" // input (wait for n + 1; mov; jmp)",
521-
" pio.txf[sm] = (125000000 / (2 * freq)) - 3",
520+
" let value = (125000000 / (2 * freq)) - 3",
521+
" switch sm {",
522+
" case 0:",
523+
" pio.pointee.txf.0 = value",
524+
" case 1:",
525+
" pio.pointee.txf.1 = value",
526+
" case 2:",
527+
" pio.pointee.txf.2 = value",
528+
" case 3:",
529+
" pio.pointee.txf.3 = value",
530+
" default:",
531+
" // There are 4 state machines available",
532+
' fatalError("Invalid state machine index")',
533+
" }",
522534
"}",
523535
),
524536
(
525537
"// PIO Blinking example",
526-
"let pio = pio0",
538+
"guard let pio = get_pio0() else {",
539+
' fatalError("PIO0 not available")',
540+
"}",
527541
"let offset = pio_add_program(pio, &blink_program)",
528-
'printf("Loaded program at %d\\n", offset)',
542+
'print("Loaded program at \\(offset)")',
529543
"",
530-
"#ifdef PICO_DEFAULT_LED_PIN",
531-
"blink_pin_forever(pio, 0, offset, PICO_DEFAULT_LED_PIN, 3)",
532-
"#else",
533-
"blink_pin_forever(pio, 0, offset, 6, 3)",
534-
"#endif",
544+
"blink_pin_forever(pio, sm: 0, offset: UInt32(offset), pin: UInt32(PICO_DEFAULT_LED_PIN), freq: 3)",
535545
"// For more pio examples see https://github.com/raspberrypi/pico-examples/tree/master/pio",
536546
),
537547
],
538548
"clocks": [
539549
(),
540550
(
541-
'printf("System Clock Frequency is %d Hz\\n", clock_get_hz(clk_sys))',
542-
'printf("USB Clock Frequency is %d Hz\\n", clock_get_hz(clk_usb))',
551+
'print("System Clock Frequency is \\(clock_get_hz(clk_sys)) Hz")',
552+
'print("USB Clock Frequency is \\(clock_get_hz(clk_usb)) Hz")',
543553
"// For more examples of clocks use see https://github.com/raspberrypi/pico-examples/tree/master/clocks",
544554
),
545555
],
@@ -608,12 +618,12 @@ def GDB_NAME():
608618
"let divisor = -321",
609619
"// This is the recommended signed fast divider for general use.",
610620
"let result = hw_divider_divmod_s32(dividend, divisor)",
611-
'printf("%d/%d = %d remainder %d\\n", dividend, divisor, to_quotient_s32(result), to_remainder_s32(result))',
621+
'print("\\(dividend)/\\(divisor) = \\(to_quotient_s32(result)) remainder \\(to_remainder_s32(result))")',
612622
"// This is the recommended unsigned fast divider for general use.",
613623
"let udividend = 123456",
614624
"let udivisor = 321",
615625
"let uresult = hw_divider_divmod_u32(udividend, udivisor)",
616-
'printf("%d/%d = %d remainder %d\\n", udividend, udivisor, to_quotient_u32(uresult), to_remainder_u32(uresult))',
626+
'printf("\\(udividend)/\\(udivisor) = \\(to_quotient_u32(uresult)) remainder \\(to_remainder_u32(uresult))")',
617627
"// See https://github.com/raspberrypi/pico-examples/tree/master/divider for more complex use",
618628
),
619629
],
@@ -629,15 +639,15 @@ def GDB_NAME():
629639
(
630640
"// Enable wifi station",
631641
"cyw43_arch_enable_sta_mode()\n",
632-
'printf("Connecting to Wi-Fi...\\n")',
642+
'print("Connecting to Wi-Fi...")',
633643
'if cyw43_arch_wifi_connect_timeout_ms("Your Wi-Fi SSID", "Your Wi-Fi Password", CYW43_AUTH_WPA2_AES_PSK, 30000) {',
634-
' printf("failed to connect.\\n")',
644+
' print("failed to connect.")',
635645
" return 1",
636646
"} else {",
637-
' printf("Connected.\\n")',
647+
' print("Connected.")',
638648
" // Read the ip address in a human readable way",
639649
" let ip_address = (uint8_t*)&(cyw43_state.netif[0].ip_addr.addr)",
640-
' printf("IP address %d.%d.%d.%d\\n", ip_address[0], ip_address[1], ip_address[2], ip_address[3])',
650+
' print("IP address \\(ip_address[0]).\\(ip_address[1]).\\(ip_address[2]).\\(ip_address[3])")',
641651
"}",
642652
),
643653
],
@@ -701,6 +711,9 @@ def codeSdkPath(sdkVersion):
701711
return f"${{userHome}}{relativeSDKPath(sdkVersion)}"
702712

703713

714+
CODE_BASIC_TOOLCHAIN_PATH = "${userHome}/.pico-sdk/toolchain"
715+
716+
704717
def codeOpenOCDPath(openocdVersion):
705718
return f"${{userHome}}{relativeOpenOCDPath(openocdVersion)}"
706719

@@ -936,23 +949,23 @@ def GenerateMain(folder, projectName, features, cpp, wantEntryProjName, swift):
936949
if len(features_list[feat][H_FILE]) == 0:
937950
continue
938951
o = f'#include "{features_list[feat][H_FILE]}"\n'
939-
if swift:
952+
if swift and bridging_file:
940953
bridging_file.write(o)
941954
else:
942955
file.write(o)
943956
if feat in stdlib_examples_list:
944957
if len(stdlib_examples_list[feat][H_FILE]) == 0:
945958
continue
946959
o = f'#include "{stdlib_examples_list[feat][H_FILE]}"\n'
947-
if swift:
960+
if swift and bridging_file:
948961
bridging_file.write(o)
949962
else:
950963
file.write(o)
951964
if feat in picow_options_list:
952965
if len(picow_options_list[feat][H_FILE]) == 0:
953966
continue
954967
o = f'#include "{picow_options_list[feat][H_FILE]}"\n'
955-
if swift:
968+
if swift and bridging_file:
956969
bridging_file.write(o)
957970
else:
958971
file.write(o)
@@ -969,7 +982,7 @@ def GenerateMain(folder, projectName, features, cpp, wantEntryProjName, swift):
969982
for feat in features:
970983
if feat in frags:
971984
for s in frags[feat][DEFINES]:
972-
if swift and s.startswith("#include"):
985+
if swift and bridging_file and s.startswith("#include"):
973986
bridging_file.write(s)
974987
bridging_file.write("\n")
975988
file.write(s)
@@ -1044,7 +1057,10 @@ def GenerateMain(folder, projectName, features, cpp, wantEntryProjName, swift):
10441057

10451058
file.write(main)
10461059

1047-
bridging_file.close()
1060+
if bridging_file:
1061+
bridging_file.write("// always include last\n")
1062+
bridging_file.write("#include <bridge.h>\n\n")
1063+
bridging_file.close()
10481064
file.close()
10491065

10501066

@@ -1211,9 +1227,17 @@ def GenerateCMake(folder, params):
12111227
entry_point_file_name = projectName if params["wantEntryProjName"] else "main"
12121228

12131229
if params["wantCPP"]:
1214-
file.write(f"add_executable({projectName} {entry_point_file_name}.cpp )\n\n")
1230+
file.write(f"add_executable({projectName} {entry_point_file_name}.cpp)\n\n")
12151231
elif params["useSwift"]:
1216-
file.write(f"add_executable({projectName})\n\n")
1232+
file.write(
1233+
f"add_executable({projectName} ${{USERHOME}}/.pico-sdk/toolchain/bridge.c)\n\n"
1234+
)
1235+
1236+
# Standard libraries
1237+
file.write("# Add the standard library to the build\n")
1238+
file.write(f"target_link_libraries({projectName}\n")
1239+
file.write(" " + STANDARD_LIBRARIES)
1240+
file.write(")\n\n")
12171241

12181242
main_file_name = f"{entry_point_file_name}.swift"
12191243
cmake_custom_swift_command = (
@@ -1225,7 +1249,7 @@ def GenerateCMake(folder, params):
12251249
" get_property(visited_targets GLOBAL PROPERTY visited_targets)\n\n"
12261250
" # make sure we don't visit the same target twice\n"
12271251
" # and that we don't visit the special generator expressions\n"
1228-
' if (${target} MATCHES "\\$<" OR ${target} MATCHES "::@" OR ${target} IN_LIST visited_targets)\n'
1252+
' if (${target} MATCHES "\\\\$<" OR ${target} MATCHES "::@" OR ${target} IN_LIST visited_targets)\n'
12291253
" return()\n"
12301254
" endif()\n\n"
12311255
" # Append the target to visited_targets\n"
@@ -1263,11 +1287,11 @@ def GenerateCMake(folder, params):
12631287
f" $$\( echo '$<TARGET_PROPERTY:{projectName},INCLUDE_DIRECTORIES>' | tr '\;' '\\\\n' | sed -e 's/\\\\\(.*\\\\\)/-Xcc -I\\\\1/g' \)\n"
12641288
" $$\( echo '${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}' | tr ' ' '\\\\n' | sed -e 's/\\\\\(.*\\\\\)/-Xcc -I\\\\1/g' \)\n"
12651289
" -import-bridging-header ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h\n"
1266-
f" ${{CMAKE_CURRENT_LIST_DIR}}/{entry_point_file_name}.swift\n"
1290+
f" ${{CMAKE_CURRENT_LIST_DIR}}/{main_file_name}\n"
12671291
" -c -o ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o\n"
12681292
" DEPENDS\n"
12691293
" ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h\n"
1270-
f" ${{CMAKE_CURRENT_LIST_DIR}}/{entry_point_file_name}.swift\n"
1294+
f" ${{CMAKE_CURRENT_LIST_DIR}}/{main_file_name}\n"
12711295
")\n"
12721296
)
12731297
file.write(cmake_custom_swift_command)
@@ -1322,18 +1346,26 @@ def GenerateCMake(folder, params):
13221346
file.write(f'WIFI_PASSWORD="${WIFI_PASSWORD}"')
13231347
file.write(")\n\n")
13241348

1325-
# Standard libraries
1326-
file.write("# Add the standard library to the build\n")
1327-
file.write(f"target_link_libraries({projectName}\n")
1328-
file.write(" " + STANDARD_LIBRARIES)
1329-
if params["useSwift"]:
1330-
file.write(" ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o\n")
1331-
file.write(")\n\n")
1349+
if not params["useSwift"]:
1350+
# Standard libraries
1351+
file.write("# Add the standard library to the build\n")
1352+
file.write(f"target_link_libraries({projectName}\n")
1353+
file.write(" " + STANDARD_LIBRARIES)
1354+
file.write(")\n\n")
1355+
else:
1356+
file.write("# Add the swift artifact to the build\n")
1357+
file.write(f"target_link_libraries({projectName}\n")
1358+
file.write(" ${CMAKE_CURRENT_BINARY_DIR}/_swiftcode.o\n")
1359+
file.write(")\n\n")
13321360

13331361
# Standard include directories
13341362
file.write("# Add the standard include files to the build\n")
13351363
file.write(f"target_include_directories({projectName} PRIVATE\n")
1336-
file.write(" " + "${CMAKE_CURRENT_LIST_DIR}\n")
1364+
file.write(" ${CMAKE_CURRENT_LIST_DIR}\n")
1365+
if params["useSwift"]:
1366+
# bridge.h includes serveral helper functions
1367+
# for swift-c-pico-sdk interop
1368+
file.write(" ${USERHOME}/.pico-sdk/toolchain\n")
13371369
file.write(")\n\n")
13381370

13391371
if params["useSwift"]:
@@ -1494,7 +1526,8 @@ def generateProjectFiles(
14941526
"name": "Pico",
14951527
"includePath": [
14961528
"${{workspaceFolder}}/**",
1497-
"{codeSdkPath(sdkVersion)}/**"
1529+
"{codeSdkPath(sdkVersion)}/**"{f""",
1530+
"{CODE_BASIC_TOOLCHAIN_PATH}\"""" if useSwift else ""}
14981531
],
14991532
"forcedInclude": [
15001533
"{codeSdkPath(sdkVersion)}/src/common/{base_headers_folder_name}/include/pico.h",

0 commit comments

Comments
 (0)