Skip to content

Commit

Permalink
Fix monotonic_time/1 and system_time/1 with pico (fix #786)
Browse files Browse the repository at this point in the history
Fix a bug where setting the time with RTC wouldn't set system time.
Also fix a bug with avm_data revealed by new tests.

Signed-off-by: Paul Guyot <[email protected]>
  • Loading branch information
pguyot committed Sep 4, 2023
1 parent 261a031 commit 72d635d
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 30 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/pico-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
uses: actions/checkout@v3

- name: "Install deps"
run: sudo apt install -y cmake gperf ninja-build gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
run: sudo apt install -y cmake gperf ninja-build gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib erlang-base erlang-dialyzer

- name: Build
shell: bash
Expand All @@ -58,4 +58,4 @@ jobs:
source $HOME/.nvm/nvm.sh
nvm use node
npm install
npx tsx run-tests.ts ../build/tests/rp2040_tests.uf2
npx tsx run-tests.ts ../build/tests/rp2040_tests.uf2 ../build/tests/test_erl_sources/rp2040_test_modules.uf2
2 changes: 1 addition & 1 deletion doc/src/build-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ Install the emulator and required Javascript dependencies:

We are assuming tests were built as part of regular build of AtomVM. Run them with the commands:

shell$ npx tsx run-tests.ts ../build/tests/rp2040_tests.uf2
shell$ npx tsx run-tests.ts ../build/tests/rp2040_tests.uf2 ../build/tests/test_erl_sources/rp2040_test_modules.uf2

## Building for NodeJS/Web

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ add_library(esp32_test_modules)

ExternalProject_Add(HostAtomVM
SOURCE_DIR ../../../../../../../../
INSTALL_COMMAND cmake -E echo "Skipping install step."
)

function(compile_erlang module_name)
Expand Down
25 changes: 25 additions & 0 deletions src/platforms/rp2040/src/lib/platform_nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#pragma GCC diagnostic ignored "-Wpedantic"

#include <hardware/rtc.h>
#include <sys/time.h>

#pragma GCC diagnostic pop

Expand Down Expand Up @@ -96,15 +97,39 @@ static term nif_pico_rtc_set_datetime(Context *ctx, int argc, term argv[])
pico_datetime.year = term_to_int(year);
pico_datetime.month = term_to_int(month);
pico_datetime.day = term_to_int(day);
pico_datetime.dotw = 0;
pico_datetime.hour = term_to_int(hour);
pico_datetime.min = term_to_int(min);
pico_datetime.sec = term_to_int(sec);

if (UNLIKELY(!rtc_running())) {
rtc_init();
}
if (UNLIKELY(!rtc_set_datetime(&pico_datetime))) {
printf("rtc_set_datetime failed\n");
argv[0] = ERROR_ATOM;
argv[1] = BADARG_ATOM;
return term_invalid_term();
}
// Wait until RTC is done
sleep_us(64);

// Update epoch_time_us_since_boot
rtc_get_datetime(&pico_datetime);
struct tm c11_time;
memset(&c11_time, 0, sizeof(c11_time));
c11_time.tm_sec = pico_datetime.sec;
c11_time.tm_min = pico_datetime.min;
c11_time.tm_hour = pico_datetime.hour;
c11_time.tm_mday = pico_datetime.day;
c11_time.tm_mon = pico_datetime.month - 1;
c11_time.tm_year = pico_datetime.year - 1900;
c11_time.tm_wday = pico_datetime.dotw;

struct timeval tv;
tv.tv_sec = mktime(&c11_time);
tv.tv_usec = 0;
settimeofday(&tv, NULL);

return OK_ATOM;
}
Expand Down
4 changes: 3 additions & 1 deletion src/platforms/rp2040/src/lib/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

#pragma GCC diagnostic pop

#include <scheduler.h>
#include <utils.h>

struct Mutex
Expand All @@ -56,7 +57,8 @@ static void scheduler_core1_entry_point(void)
{
_Static_assert(sizeof(uintptr_t) == sizeof(uint32_t), "Expected pointers to be 32 bits");
uint32_t ctx_int = multicore_fifo_pop_blocking();
return scheduler_entry_point((GlobalContext *) ctx_int);
int result = scheduler_entry_point((GlobalContext *) ctx_int);
UNUSED(result);
}

void smp_scheduler_start(GlobalContext *ctx)
Expand Down
49 changes: 27 additions & 22 deletions src/platforms/rp2040/src/lib/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <hardware/rtc.h>
#include <pico/multicore.h>
#include <pico/time.h>
#include <sys/time.h>

#pragma GCC diagnostic pop

Expand All @@ -42,6 +43,22 @@

struct NifCollectionDefListItem *nif_collection_list;

static uint64_t rtc_init_usec = 0;

bool sys_rtc_set_datetime(datetime_t *t)
{
if (UNLIKELY(!rtc_set_datetime(t))) {
return false;
};
// Reset the usec counter
absolute_time_t now = get_absolute_time();
rtc_init_usec = to_us_since_boot(now);
// rtc_set_datetime takes one RTC cycle
sleep_us(64);

return true;
}

void sys_init_platform(GlobalContext *glb)
{
struct RP2040PlatformData *platform = malloc(sizeof(struct RP2040PlatformData));
Expand Down Expand Up @@ -101,31 +118,18 @@ void sys_unregister_select_event(GlobalContext *global, ErlNifEvent event, bool

void sys_time(struct timespec *t)
{
if (!rtc_running()) {
rtc_init();
sleep_us(64);
}
datetime_t pico_datetime;
rtc_get_datetime(&pico_datetime);
struct tm c11_time;
memset(&c11_time, 0, sizeof(c11_time));
c11_time.tm_sec = pico_datetime.sec;
c11_time.tm_min = pico_datetime.min;
c11_time.tm_hour = pico_datetime.hour;
c11_time.tm_mday = pico_datetime.day;
c11_time.tm_mon = pico_datetime.month - 1;
c11_time.tm_year = pico_datetime.year - 1900;
c11_time.tm_wday = pico_datetime.dotw;
t->tv_sec = mktime(&c11_time);

absolute_time_t now = get_absolute_time();
uint64_t usec = to_us_since_boot(now);
t->tv_nsec = (usec % 1000000) * 1000;
struct timeval tv;
gettimeofday(&tv, NULL);
t->tv_sec = tv.tv_sec;
t->tv_nsec = tv.tv_usec * 1000;
}

void sys_monotonic_time(struct timespec *t)
{
sys_time(t);
absolute_time_t now = get_absolute_time();
uint64_t usec = to_us_since_boot(now);
t->tv_nsec = (usec % 1000000) * 1000;
t->tv_sec = (usec / 1000000);
}

uint64_t sys_monotonic_millis()
Expand All @@ -140,6 +144,7 @@ enum OpenAVMResult sys_open_avm_from_file(
{
UNUSED(global);
UNUSED(path);
UNUSED(data);

// TODO
return AVM_OPEN_NOT_SUPPORTED;
Expand All @@ -162,7 +167,7 @@ Module *sys_load_module(GlobalContext *global, const char *module_name)
struct ListHead *item;
struct ListHead *avmpack_data = synclist_rdlock(&global->avmpack_data);
LIST_FOR_EACH (item, avmpack_data) {
struct AVMPackData *avmpack_data = (struct AVMPackData *) item;
struct AVMPackData *avmpack_data = GET_LIST_ENTRY(item, struct AVMPackData, avmpack_head);
if (avmpack_find_section_by_name(avmpack_data->data, module_name, &beam_module, &beam_module_size)) {
break;
}
Expand Down
8 changes: 5 additions & 3 deletions src/platforms/rp2040/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,21 +101,23 @@ static int app_main()
AVM_ABORT();
}

avmpack_data_init(&avmpack_data->base, &const_avm_pack_info);
avmpack_data->base.data = MAIN_AVM;
avmpack_data->base.in_use = true;

synclist_append(&glb->avmpack_data, (struct ListHead *) avmpack_data);
synclist_append(&glb->avmpack_data, &avmpack_data->base.avmpack_head);

if (avmpack_is_valid(LIB_AVM, (uintptr_t) MAIN_AVM - (uintptr_t) LIB_AVM)) {
avmpack_data = malloc(sizeof(struct AVMPackData));
avmpack_data = malloc(sizeof(struct ConstAVMPack));
if (IS_NULL_PTR(avmpack_data)) {
sleep_ms(5000);
fprintf(stderr, "Memory error: Cannot allocate AVMPackData for lib.avm.");
AVM_ABORT();
}
avmpack_data_init(&avmpack_data->base, &const_avm_pack_info);
avmpack_data->base.data = LIB_AVM;
avmpack_data->base.in_use = true;
synclist_append(&glb->avmpack_data, (struct ListHead *) avmpack_data);
synclist_append(&glb->avmpack_data, &avmpack_data->base.avmpack_head);
} else {
fprintf(stderr, "Warning: invalid lib.avm packbeam\n");
}
Expand Down
3 changes: 3 additions & 0 deletions src/platforms/rp2040/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ pico_enable_stdio_uart(AtomVM 1)

# create map/bin/hex/uf2 file in addition to ELF.
pico_add_extra_outputs(rp2040_tests)

add_subdirectory(test_erl_sources)
add_dependencies(rp2040_tests rp2040_test_modules)
72 changes: 72 additions & 0 deletions src/platforms/rp2040/tests/test_erl_sources/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#
# This file is part of AtomVM.
#
# Copyright 2023 Paul Guyot <[email protected]>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

ExternalProject_Add(HostAtomVM
SOURCE_DIR ../../../../../../
INSTALL_COMMAND cmake -E echo "Skipping install step."
)

function(compile_erlang module_name)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.beam"
COMMAND erlc ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.erl"
COMMENT "Compiling ${module_name}.erl"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/${module_name}.beam")
endfunction()

compile_erlang(test_clocks)
compile_erlang(test_smp)

add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.avm"
COMMAND HostAtomVM-prefix/src/HostAtomVM-build/tools/packbeam/PackBEAM -i rp2040_test_modules.avm
HostAtomVM-prefix/src/HostAtomVM-build/libs/atomvmlib.avm
test_clocks.beam
test_smp.beam
DEPENDS
HostAtomVM
"${CMAKE_CURRENT_BINARY_DIR}/test_clocks.beam"
"${CMAKE_CURRENT_BINARY_DIR}/test_smp.beam"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM
)

set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.avm")

add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.uf2"
COMMAND HostAtomVM-prefix/src/HostAtomVM-build/tools/uf2tool/uf2tool
create
-o rp2040_test_modules.uf2
-s 0x100A0000 "${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.avm"
DEPENDS
HostAtomVM
"${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.avm"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM
)

set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.uf2")

add_custom_target(rp2040_test_modules DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/rp2040_test_modules.uf2")
54 changes: 54 additions & 0 deletions src/platforms/rp2040/tests/test_erl_sources/test_clocks.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
%
% This file is part of AtomVM.
%
% Copyright 2023 Paul Guyot <[email protected]>
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
%

-module(test_clocks).
-export([start/0]).

-define(START_DATE, {{2023, 9, 3}, {21, 30, 00}}).

start() ->
test_clock(system_time, fun() -> erlang:system_time(millisecond) end),
test_clock(monotonic_time, fun() -> erlang:monotonic_time(millisecond) end),
Date = erlang:universaltime(),
if
Date < ?START_DATE ->
pico:rtc_set_datetime(?START_DATE);
true ->
ok
end,
test_clock(system_time_after_set_rtc, fun() -> erlang:system_time(millisecond) end),
NewDate = erlang:universaltime(),
if
NewDate >= ?START_DATE -> ok;
true -> throw({unexpected_date, NewDate})
end,
ok.

test_clock(Case, Fun) ->
StartTime = Fun(),
receive
after 100 -> ok
end,
EndTime = Fun(),
Delta = EndTime - StartTime,
if
Delta >= 100 andalso Delta < 500 -> ok;
true -> throw({unexpected_delta, Delta, Case, StartTime, EndTime})
end.
51 changes: 51 additions & 0 deletions src/platforms/rp2040/tests/test_erl_sources/test_smp.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
%
% This file is part of AtomVM.
%
% Copyright 2023 Paul Guyot <[email protected]>
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
%

-module(test_smp).
-export([start/0]).

start() ->
{Pid1, Monitor1} = spawn_opt(fun count_loop/0, [monitor]),
{Pid2, Monitor2} = spawn_opt(fun count_loop/0, [monitor]),
{Pid3, Monitor3} = spawn_opt(fun count_loop/0, [monitor]),
{Pid4, Monitor4} = spawn_opt(fun count_loop/0, [monitor]),
receive
{'DOWN', Monitor1, process, Pid1, normal} -> ok
end,
receive
{'DOWN', Monitor2, process, Pid2, normal} -> ok
end,
receive
{'DOWN', Monitor3, process, Pid3, normal} -> ok
end,
receive
{'DOWN', Monitor4, process, Pid4, normal} -> ok
end,
ok.

count_loop() ->
count_loop(1000).

count_loop(N) when N < 0 -> ok;
count_loop(X) ->
case X rem 2 of
0 -> count_loop(X - 1);
1 -> count_loop(X - 3)
end.
Loading

0 comments on commit 72d635d

Please sign in to comment.