[#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;
+}