[#59552] ExternalControl: Add getting current virtual time
diff --git a/src/Renode/Network/ExternalControl/ExternalControlServer.cs b/src/Renode/Network/ExternalControl/ExternalControlServer.cs index 00f7f6d..f786f9a 100644 --- a/src/Renode/Network/ExternalControl/ExternalControlServer.cs +++ b/src/Renode/Network/ExternalControl/ExternalControlServer.cs
@@ -35,6 +35,7 @@ commandHandlers = new CommandHandlerCollection(); commandHandlers.Register(new RunFor(this)); + commandHandlers.Register(new GetTime(this)); socketServerProvider.ConnectionAccepted += delegate {
diff --git a/src/Renode/Network/ExternalControl/GetTime.cs b/src/Renode/Network/ExternalControl/GetTime.cs new file mode 100644 index 0000000..0c96b5d --- /dev/null +++ b/src/Renode/Network/ExternalControl/GetTime.cs
@@ -0,0 +1,32 @@ +// +// Copyright (c) 2010-2024 Antmicro +// +// This file is licensed under the MIT License. +// Full license text is available in 'licenses/MIT.txt'. +// +using System.Collections.Generic; +using Antmicro.Renode.Core; +using Antmicro.Renode.Logging; +using Antmicro.Renode.Utilities; + +namespace Antmicro.Renode.Network.ExternalControl +{ + public class GetTime : BaseCommand + { + public GetTime(IEmulationElement parent) + : base(parent) + { + } + + public override Response Invoke(List<byte> data) + { + var timestamp = EmulationManager.Instance.CurrentEmulation.MasterTimeSource.ElapsedVirtualTime; + parent.Log(LogLevel.Info, "Executing GetTime command: {0}", timestamp); + + return Response.Success(Identifier, timestamp.TotalMicroseconds.AsRawBytes()); + } + + public override Command Identifier => Command.GetTime; + public override byte Version => 0x0; + } +}
diff --git a/src/Renode/Network/ExternalControl/ICommand.cs b/src/Renode/Network/ExternalControl/ICommand.cs index dd0086a..f58f928 100644 --- a/src/Renode/Network/ExternalControl/ICommand.cs +++ b/src/Renode/Network/ExternalControl/ICommand.cs
@@ -11,6 +11,7 @@ public enum Command : byte { RunFor = 1, + GetTime, } public interface ICommand
diff --git a/src/Renode/Renode.csproj b/src/Renode/Renode.csproj index 9be62b6..a558c2a 100644 --- a/src/Renode/Renode.csproj +++ b/src/Renode/Renode.csproj
@@ -134,6 +134,7 @@ <Compile Include="Network\ExternalControl\ICommand.cs" /> <Compile Include="Network\ExternalControl\Response.cs" /> <Compile Include="Network\ExternalControl\RunFor.cs" /> + <Compile Include="Network\ExternalControl\GetTime.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ProjectExtensions>
diff --git a/tools/external_control_client/README.md b/tools/external_control_client/README.md index 810f3d4..7817bd8 100644 --- a/tools/external_control_client/README.md +++ b/tools/external_control_client/README.md
@@ -27,6 +27,7 @@ * `renode_connect` * `renode_disconnect` * `renode_run_for` +* `renode_get_current_time` * `renode_error_free` The library itself can be built with CMake using the `./lib/CMakeLists.txt`.
diff --git a/tools/external_control_client/examples/run_for/main.c b/tools/external_control_client/examples/run_for/main.c index 4564142..40f89b0 100644 --- a/tools/external_control_client/examples/run_for/main.c +++ b/tools/external_control_client/examples/run_for/main.c
@@ -33,6 +33,19 @@ return error->message; } +uint64_t get_current_virtual_time(renode_t *renode) +{ + renode_error_t *error; + uint64_t current_time; + if ((error = renode_get_current_time(renode, TU_MICROSECONDS, ¤t_time)) != NO_ERROR) { + fprintf(stderr, "Get current time failed with: %s\n", get_error_message(error)); + renode_free_error(error); + exit(EXIT_FAILURE); + } + + return current_time; +} + int main(int argc, char **argv) { if (argc != 3) { @@ -74,17 +87,36 @@ exit(EXIT_FAILURE); } + uint64_t t0 = get_current_virtual_time(renode); + if ((error = renode_run_for(renode, time_unit, value)) != NO_ERROR) { fprintf(stderr, "Run for failed with: %s\n", get_error_message(error)); renode_free_error(error); exit(EXIT_FAILURE); } + uint64_t t1 = get_current_virtual_time(renode); + uint64_t t_delta = t1 - t0; + if ((error = renode_disconnect(&renode)) != NO_ERROR) { fprintf(stderr, "Disconnecting from Renode failed with: %s\n", get_error_message(error)); renode_free_error(error); exit(EXIT_FAILURE); } + int microseconds = t1 % TU_SECONDS; + int seconds_total = t1 / TU_SECONDS; + int seconds = seconds_total % 60; + int minutes_total = seconds_total / 60; + int minutes = minutes_total % 60; + int hours_total = minutes_total / 60; + + printf("Elapsed virtual time %02d:%02d:%02d.%06d\n", hours_total, minutes, seconds, microseconds); + + if (t_delta != value * time_unit) { + fprintf(stderr, "Reported current virtual time doesn't match the expected virtual time after running for the requested interval\n"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); }
diff --git a/tools/external_control_client/include/renode_api.h b/tools/external_control_client/include/renode_api.h index 8d67831..c333f4f 100644 --- a/tools/external_control_client/include/renode_api.h +++ b/tools/external_control_client/include/renode_api.h
@@ -47,3 +47,4 @@ } renode_time_unit_t; renode_error_t *renode_run_for(renode_t *renode, renode_time_unit_t unit, uint64_t value); +renode_error_t *renode_get_current_time(renode_t *renode_instance, renode_time_unit_t unit, uint64_t *current_time);
diff --git a/tools/external_control_client/lib/renode_api.c b/tools/external_control_client/lib/renode_api.c index b45de96..7f286ea 100644 --- a/tools/external_control_client/lib/renode_api.c +++ b/tools/external_control_client/lib/renode_api.c
@@ -114,11 +114,13 @@ typedef enum { RUN_FOR = 1, + GET_TIME, } api_command_t; static uint8_t command_versions[][2] = { { 0x0, 0x0 }, // reserved for size { RUN_FOR, 0x0 }, + { GET_TIME, 0x0 }, }; static renode_error_t *write_or_fail(int socket_fd, const uint8_t *data, ssize_t count) @@ -400,3 +402,29 @@ return renode_execute_command(renode, RUN_FOR, &data, sizeof(data), sizeof(data), NULL); } + +renode_error_t *renode_get_current_time(renode_t *renode, renode_time_unit_t unit, uint64_t *current_time) +{ + assert(renode != NULL); + + uint64_t divider; + switch (unit) { + case TU_MICROSECONDS: + case TU_MILLISECONDS: + case TU_SECONDS: + // The enum values are equal to 1 `unit` expressed in microseconds. + divider = unit; + break; + default: + assert_fmsg(false, "Invalid unit: %d\n", unit); + } + + uint32_t response_size; + return_error_if_fails(renode_execute_command(renode, GET_TIME, current_time, sizeof(*current_time), sizeof(*current_time), &response_size)); + + assert_msg(response_size == sizeof(*current_time), "Received unexpected number of bytes"); + + *current_time /= divider; + + return NO_ERROR; +}