Skip to content

Commit be62d3a

Browse files
committed
Merge branch 'main' into feature/distributed-erlang
2 parents 32de311 + ed48c7c commit be62d3a

File tree

13 files changed

+206
-52
lines changed

13 files changed

+206
-52
lines changed

.github/workflows/build-and-test-on-freebsd.yaml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,17 @@ concurrency:
3434

3535
jobs:
3636
build-and-test-on-freebsd:
37-
runs-on: ubuntu-22.04
37+
runs-on: ubuntu-24.04
3838
name: Build and test AtomVM on FreeBSD
3939
env:
4040
ATOMVM_EXAMPLE: "atomvm-example"
41+
42+
strategy:
43+
fail-fast: false
44+
45+
matrix:
46+
os_release: ["13.4", "14.2"]
47+
4148
steps:
4249

4350
- uses: actions/checkout@v4
@@ -47,17 +54,17 @@ jobs:
4754
uses: vmactions/freebsd-vm@v1
4855
timeout-minutes: 20
4956
with:
50-
release: 13.2
57+
release: ${{ matrix.os_release }}
5158
envs: 'ATOMVM_EXAMPLE'
5259
usesh: true
5360
sync: rsync
5461
copyback: false
5562

5663
prepare: |
57-
pkg install -y curl cmake gperf erlang elixir mbedtls
64+
pkg install -y curl cmake gperf erlang elixir rebar3 mbedtls
5865
5966
run: |
60-
67+
set -e
6168
echo "%%"
6269
echo "%% System Info"
6370
echo "%%"

.github/workflows/build-and-test-other.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ jobs:
107107
# Required for testing big endian archs
108108
- arch: "s390x"
109109
platform: "s390x"
110-
tag: "bullseye"
110+
tag: "bookworm"
111111
cflags: "-O2"
112112
cmake_opts: "-DAVM_WARNINGS_ARE_ERRORS=ON"
113113

.github/workflows/esp32-simtest.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ jobs:
5555
runs-on: ubuntu-24.04
5656
if: needs.cli_token.outputs.token_check == 'true'
5757
container: espressif/idf:${{ matrix.idf-version }}
58+
services:
59+
wokwi-ci-server:
60+
image: wokwi/wokwi-ci-server
61+
ports:
62+
- 3000:3000
63+
5864
strategy:
5965
fail-fast: false
6066
# focus on device diversity.
@@ -120,6 +126,10 @@ jobs:
120126
idf.py -DSDKCONFIG_DEFAULTS='sdkconfig.ci.wokwi' set-target ${{matrix.esp-idf-target}}
121127
idf.py build
122128
129+
- name: Configure Wokwi environment
130+
run: |
131+
echo "WOKWI_CLI_SERVER=ws://wokwi-ci-server:3000" >> $GITHUB_ENV
132+
123133
- name: Run ESP32-sim tests using Wokwi CI
124134
working-directory: ./src/platforms/esp32/test/
125135
env:

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ certain VM instructions are used.
6666
- Fixed a double free when esp32 uart driver was closed, yielding an assert abort
6767
- Fixed compilation with latest debian gcc-arm-none-eabi
6868
- Fix `network:stop/0` on ESP32 so the network can be started again
69+
- Fix matching of binaries on unaligned boundaries for code compiled with older versions of OTP
70+
- Fix a memory corruption caused by `binary:split/2,3`
6971

7072
## [0.6.5] - 2024-10-15
7173

doc/src/build-instructions.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,63 @@ $ pytest --embedded-services=idf,qemu -s
315315

316316
ESP32 tests are erlang modules located in `src/platforms/esp32/test/main/test_erl_sources/` and executed from `src/platforms/esp32/test/main/test_main.c`.
317317

318+
### Performance and Power
319+
320+
AtomVM comes with conservative defaults for broad compatibility with different ESP32 boards, and a reasonable performance/power/longevity tradeoff.
321+
322+
You may want to change these settings to optimize for your specific application's performance and power needs.
323+
324+
Factors like heat dissipation should also be considered, and the effect on overall longevity of components.
325+
326+
#### CPU frequency
327+
328+
Use `idf.py menuconfig` in `src/platforms/esp32`
329+
`Component config ---> ESP System Settings ---> CPU frequency (160 MHz) --->`
330+
331+
You can increase or decrease the CPU frequency, this is a tradeoff against power usage.
332+
Eg. 160 MHz is the conservative default for the ESP32, but you can increase it to 240 MHz or decrease it to 80 MHz. The higher the frequency, the more power is consumed. The lower the frequency, the less power is consumed.
333+
334+
#### Flash mode and speed
335+
336+
Use `idf.py menuconfig` in `src/platforms/esp32`
337+
`Serial flasher config ---> Flash SPI mode (DIO) --->`
338+
You can change the mode of the SPI flash. QIO is the fastest mode, but not all flash chips support it.
339+
340+
`Serial flasher config ---> Flash SPI speed (40 MHz) --->`
341+
You can change the speed of the SPI flash. The higher the speed, the faster the flash will be, at the cost of higher power usage, but not all flash chips support higher speeds.
342+
343+
See external docs: [ESP-IDF flash modes](https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/spi-flash-modes.html)
344+
345+
#### PSRAM speed
346+
347+
Use `idf.py menuconfig` in `src/platforms/esp32`
348+
`Component config ---> ESP PSRAM ---> SPI RAM config --->`
349+
350+
If your board has PSRAM and it's enabled, you can configure the SPI RAM settings here.
351+
`Set RAM clock speed (40MHz clock speed) --->`
352+
You can increase or decrease the clock speed of the PSRAM.
353+
354+
```{warning}
355+
You may have to increase "Flash SPI speed" (see above) before you can increase PSRAM speed.
356+
```
357+
358+
The higher the speed, the faster the PSRAM will be, at the cost of higher power usage, but not all PSRAM chips support higher speeds.
359+
360+
#### Sleep mode - Deep sleep
361+
362+
For low power applications, you should use the [deep sleep functionality](./programmers-guide.md#restart-and-deep-sleep) of the ESP32.
363+
364+
This will put the ESP32 into a very low power state, and it will consume very little power.
365+
You can wake the ESP32 from deep sleep using a timer, or an interrupt etc.
366+
367+
Make sure your board is suitable for low power deep sleep, some boards have voltage regulators and/or LEDs constantly draining power, also make sure sensors are powered down or in low power mode when the ESP32 is in deep sleep.
368+
369+
For persisting small amounts of data during deep sleep, you can use the [RTC memory](./programmers-guide.md#rtc-memory) of the ESP32, which is preserved during deep sleep.
370+
371+
#### Sleep mode - Light sleep
372+
373+
Usage of light sleep is untested, and no support for controlling light sleep is currently implemented. Reach out if you do any experiments and measurements.
374+
318375
### Flash Layout
319376

320377
The AtomVM Flash memory is partitioned to include areas for the above binary artifacts created from the build, as well areas for runtime information used by the ESP32 and compiled Erlang/Elixir code.

src/libAtomVM/nifs.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3278,15 +3278,19 @@ static term nif_binary_split(Context *ctx, int argc, term argv[])
32783278
size_t num_segments = 1;
32793279
const char *temp_bin_data = bin_data;
32803280
int temp_bin_size = bin_size;
3281+
size_t heap_size = 0;
32813282
do {
32823283
const char *found = (const char *) memmem(temp_bin_data, temp_bin_size, pattern_data, pattern_size);
32833284
if (!found) break;
32843285
num_segments++;
3286+
heap_size += CONS_SIZE + term_sub_binary_heap_size(argv[0], found - temp_bin_data);
32853287
int next_search_offset = found - temp_bin_data + pattern_size;
32863288
temp_bin_data += next_search_offset;
32873289
temp_bin_size -= next_search_offset;
32883290
} while (global && temp_bin_size >= pattern_size);
32893291

3292+
heap_size += CONS_SIZE + term_sub_binary_heap_size(argv[0], temp_bin_size);
3293+
32903294
term result_list = term_nil();
32913295

32923296
if (num_segments == 1) {
@@ -3299,7 +3303,7 @@ static term nif_binary_split(Context *ctx, int argc, term argv[])
32993303
}
33003304

33013305
// binary:split/2,3 always return sub binaries, except when copied binaries are as small as sub-binaries.
3302-
if (UNLIKELY(memory_ensure_free_with_roots(ctx, LIST_SIZE(num_segments, TERM_BOXED_SUB_BINARY_SIZE), 2, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
3306+
if (UNLIKELY(memory_ensure_free_with_roots(ctx, heap_size, 2, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
33033307
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
33043308
}
33053309

src/libAtomVM/opcodesswitch.h

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4711,34 +4711,57 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
47114711
#ifdef IMPL_EXECUTE_LOOP
47124712
VERIFY_IS_MATCH_STATE(src, "bs_match_string");
47134713

4714-
if (bits % 8 != 0) {
4715-
TRACE("bs_match_string: Unsupported bits size (must be evenly divisible by 8). bits=%u\n", (unsigned) bits);
4716-
RAISE_ERROR(UNSUPPORTED_ATOM);
4717-
}
4718-
avm_int_t bytes = bits / 8;
47194714
avm_int_t bs_offset = term_get_match_state_offset(src);
47204715
term bs_bin = term_get_match_state_binary(src);
47214716

4722-
if (bs_offset % 8 != 0) {
4723-
TRACE("bs_match_string: Unsupported offset (must be evenly divisible by 8). bs_offset=%li\n", bs_offset);
4724-
RAISE_ERROR(UNSUPPORTED_ATOM);
4725-
}
4726-
avm_int_t byte_offset = bs_offset / 8;
4727-
4728-
TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);
4729-
47304717
size_t remaining = 0;
47314718
const uint8_t *str = module_get_str(mod, offset, &remaining);
47324719
if (IS_NULL_PTR(str)) {
47334720
TRACE("bs_match_string: Bad offset in strings table.\n");
47344721
RAISE_ERROR(BADARG_ATOM);
47354722
}
4736-
if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
4737-
TRACE("bs_match_string: failed to match\n");
4738-
JUMP_TO_ADDRESS(mod->labels[fail]);
4723+
4724+
TRACE("bs_match_string/4, fail=%u src=%p bits=%u offset=%u\n", (unsigned) fail, (void *) src, (unsigned) bits, (unsigned) offset);
4725+
4726+
if (bits % 8 == 0 && bs_offset % 8 == 0) {
4727+
avm_int_t bytes = bits / 8;
4728+
avm_int_t byte_offset = bs_offset / 8;
4729+
4730+
if (memcmp(term_binary_data(bs_bin) + byte_offset, str, MIN(remaining, (unsigned int) bytes)) != 0) {
4731+
TRACE("bs_match_string: failed to match\n");
4732+
JUMP_TO_ADDRESS(mod->labels[fail]);
4733+
}
47394734
} else {
4740-
term_set_match_state_offset(src, bs_offset + bits);
4735+
// Compare unaligned bits
4736+
const uint8_t *bs_str = (const uint8_t *) term_binary_data(bs_bin) + (bs_offset / 8);
4737+
uint8_t bin_bit_offset = 7 - (bs_offset - (8 *(bs_offset / 8)));
4738+
uint8_t str_bit_offset = 7;
4739+
size_t remaining_bits = bits;
4740+
while (remaining_bits > 0) {
4741+
uint8_t str_ch = *str;
4742+
uint8_t bin_ch = *bs_str;
4743+
uint8_t str_ch_bit = (str_ch >> str_bit_offset) & 1;
4744+
uint8_t bin_ch_bit = (bin_ch >> bin_bit_offset) & 1;
4745+
if (str_ch_bit ^ bin_ch_bit) {
4746+
TRACE("bs_match_string: failed to match\n");
4747+
JUMP_TO_ADDRESS(mod->labels[fail]);
4748+
}
4749+
if (str_bit_offset) {
4750+
str_bit_offset--;
4751+
} else {
4752+
str_bit_offset = 7;
4753+
str++;
4754+
}
4755+
if (bin_bit_offset) {
4756+
bin_bit_offset--;
4757+
} else {
4758+
bin_bit_offset = 7;
4759+
bs_str++;
4760+
}
4761+
remaining_bits--;
4762+
}
47414763
}
4764+
term_set_match_state_offset(src, bs_offset + bits);
47424765
#endif
47434766
break;
47444767
}

src/libAtomVM/otp_net.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,10 @@ static term eai_errno_to_term(int err, GlobalContext *glb)
8686
#ifdef HAVE_EXTENDED_EAI_ERRNO
8787
case EAI_BADHINTS:
8888
return globalcontext_make_atom(glb, ATOM_STR("\xB", "eaibadhints"));
89+
#ifdef HAVE_EAI_OVERFLOW
8990
case EAI_OVERFLOW:
9091
return globalcontext_make_atom(glb, ATOM_STR("\xB", "eaioverflow"));
92+
#endif
9193
case EAI_PROTOCOL:
9294
return globalcontext_make_atom(glb, ATOM_STR("\xB", "eaiprotocol"));
9395
case EAI_SYSTEM:

src/libAtomVM/term.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1257,7 +1257,7 @@ static inline term term_from_literal_binary(const void *data, size_t size, Heap
12571257
*/
12581258
static inline size_t term_sub_binary_heap_size(term binary, size_t len)
12591259
{
1260-
if (term_is_refc_binary(binary) && len >= SUB_BINARY_MIN) {
1260+
if ((term_is_refc_binary(binary) || term_is_sub_binary(binary)) && len >= SUB_BINARY_MIN) {
12611261
return TERM_BOXED_SUB_BINARY_SIZE;
12621262
} else {
12631263
return term_binary_heap_size(len);

src/platforms/generic_unix/lib/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ check_symbol_exists(EAI_BADHINTS "netdb.h" HAVE_EXTENDED_EAI_ERRNO)
104104
if (HAVE_EXTENDED_EAI_ERRNO)
105105
target_compile_definitions(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC HAVE_EXTENDED_EAI_ERRNO)
106106
endif()
107+
check_symbol_exists(EAI_OVERFLOW "netdb.h" HAVE_EAI_OVERFLOW)
108+
if (HAVE_EAI_OVERFLOW)
109+
target_compile_definitions(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC HAVE_EAI_OVERFLOW)
110+
endif()
107111

108112
if (COVERAGE)
109113
include(CodeCoverage)

0 commit comments

Comments
 (0)