[#59551] ExternalControl: Add ADC support
diff --git a/src/Infrastructure b/src/Infrastructure
index 39fab07..dba5b21 160000
--- a/src/Infrastructure
+++ b/src/Infrastructure
@@ -1 +1 @@
-Subproject commit 39fab0786e6da658dd709f1691803e9274f6cea8
+Subproject commit dba5b2134eb75b659468a8c98bc2de7b9631f558
diff --git a/src/Renode/Network/ExternalControl/ADC.cs b/src/Renode/Network/ExternalControl/ADC.cs
new file mode 100644
index 0000000..cc8a9ce
--- /dev/null
+++ b/src/Renode/Network/ExternalControl/ADC.cs
@@ -0,0 +1,101 @@
+//
+// Copyright (c) 2010-2024 Antmicro
+//
+// This file is licensed under the MIT License.
+// Full license text is available in 'licenses/MIT.txt'.
+//
+using System;
+using System.Collections.Generic;
+using Antmicro.Renode.Logging;
+using Antmicro.Renode.Peripherals.Sensor;
+using Antmicro.Renode.Utilities;
+
+namespace Antmicro.Renode.Network.ExternalControl
+{
+ public class ADC : BaseCommand, IInstanceBasedCommand<IADC>
+ {
+ public ADC(ExternalControlServer parent)
+ : base(parent)
+ {
+ Instances = new InstanceCollection<IADC>();
+ }
+
+ public override Response Invoke(List<byte> data) => this.InvokeHandledWithInstance(data);
+
+ public Response Invoke(IADC instance, List<byte> data)
+ {
+ if(data.Count < 1)
+ {
+ return Response.CommandFailed(Identifier, $"Expected at least {1 + InstanceBasedCommandHeaderSize} bytes of payload");
+ }
+ var command = (ADCCommand)data[0];
+
+ var expectedCount = GetExpectedPayloadCount(command);
+ if(expectedCount != data.Count)
+ {
+ return Response.CommandFailed(Identifier, $"Expected {expectedCount + InstanceBasedCommandHeaderSize} bytes of payload");
+ }
+
+ switch(command)
+ {
+ case ADCCommand.GetCount:
+ var channelCount = instance.ADCChannelCount;
+ parent.Log(LogLevel.Debug, "Executing ADC GetCount command, returned {0}", channelCount);
+ return Response.Success(Identifier, channelCount.AsRawBytes());
+
+ case ADCCommand.GetValue:
+ DecodeChannelArgument(data, out var channel);
+ var value = instance.GetADCValue(channel);
+ parent.Log(LogLevel.Debug, "Executing ADC GetValue command, channel #{0} returned {1}", channel, value);
+ return Response.Success(Identifier, value.AsRawBytes());
+
+ case ADCCommand.SetValue:
+ DecodeSetValueArguments(data, out channel, out value);
+ parent.Log(LogLevel.Debug, "Executing ADC SetValue command, channel #{0} set to {1}", channel, value);
+ instance.SetADCValue(channel, value);
+ return Response.Success(Identifier);
+
+ default:
+ return Response.CommandFailed(Identifier, "Unexpected command format");
+ }
+ }
+
+ public InstanceCollection<IADC> Instances { get; }
+
+ public override Command Identifier => Command.ADC;
+ public override byte Version => 0x0;
+
+ private int GetExpectedPayloadCount(ADCCommand command)
+ {
+ switch(command)
+ {
+ case ADCCommand.GetValue:
+ return sizeof(byte) + sizeof(uint);
+ case ADCCommand.SetValue:
+ return sizeof(byte) + sizeof(uint) * 2;
+ default:
+ return sizeof(byte);
+ }
+ }
+
+ private void DecodeChannelArgument(List<byte> data, out int channel)
+ {
+ channel = BitConverter.ToInt32(data.GetRange(1, sizeof(uint)).ToArray(), 0);
+ }
+
+ private void DecodeSetValueArguments(List<byte> data, out int channel, out uint value)
+ {
+ DecodeChannelArgument(data, out channel);
+ value = BitConverter.ToUInt32(data.GetRange(5, sizeof(uint)).ToArray(), 0);
+ }
+
+ private const int InstanceBasedCommandHeaderSize = IInstanceBasedCommandExtensions.HeaderSize;
+
+ private enum ADCCommand : byte
+ {
+ GetCount = 0,
+ GetValue,
+ SetValue,
+ }
+ }
+}
diff --git a/src/Renode/Network/ExternalControl/ExternalControlServer.cs b/src/Renode/Network/ExternalControl/ExternalControlServer.cs
index 7a11a30..b2dc9c9 100644
--- a/src/Renode/Network/ExternalControl/ExternalControlServer.cs
+++ b/src/Renode/Network/ExternalControl/ExternalControlServer.cs
@@ -36,6 +36,7 @@
commandHandlers = new CommandHandlerCollection();
commandHandlers.Register(new RunFor(this));
commandHandlers.Register(new GetTime(this));
+ commandHandlers.Register(new ADC(this));
var getMachineHandler = new GetMachine(this);
Machines = getMachineHandler;
diff --git a/src/Renode/Network/ExternalControl/ICommand.cs b/src/Renode/Network/ExternalControl/ICommand.cs
index d7e8e54..3410b94 100644
--- a/src/Renode/Network/ExternalControl/ICommand.cs
+++ b/src/Renode/Network/ExternalControl/ICommand.cs
@@ -13,6 +13,7 @@
RunFor = 1,
GetTime,
GetMachine,
+ ADC,
}
public interface ICommand
diff --git a/src/Renode/Renode.csproj b/src/Renode/Renode.csproj
index 6001409..0cab438 100644
--- a/src/Renode/Renode.csproj
+++ b/src/Renode/Renode.csproj
@@ -137,6 +137,7 @@
<Compile Include="Network\ExternalControl\GetTime.cs" />
<Compile Include="Network\ExternalControl\IInstanceBasedCommand.cs" />
<Compile Include="Network\ExternalControl\GetMachine.cs" />
+ <Compile Include="Network\ExternalControl\ADC.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
diff --git a/tools/external_control_client/include/renode_api.h b/tools/external_control_client/include/renode_api.h
index 0993434..0177eeb 100644
--- a/tools/external_control_client/include/renode_api.h
+++ b/tools/external_control_client/include/renode_api.h
@@ -33,6 +33,7 @@
// Internals of these structs aren't part of the API.
typedef struct renode renode_t;
typedef struct renode_machine renode_machine_t;
+typedef struct renode_adc renode_adc_t;
renode_error_t *renode_connect(const char *port, renode_t **renode);
renode_error_t *renode_disconnect(renode_t **renode);
@@ -51,3 +52,10 @@
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);
+
+/* ADC */
+
+renode_error_t *renode_get_adc(renode_machine_t *machine, const char *name, renode_adc_t **adc);
+renode_error_t *renode_get_adc_channel_count(renode_adc_t *adc, int32_t *count);
+renode_error_t *renode_get_adc_channel_value(renode_adc_t *adc, int32_t channel, uint32_t *value);
+renode_error_t *renode_set_adc_channel_value(renode_adc_t *adc, int32_t channel, uint32_t value);
diff --git a/tools/external_control_client/lib/renode_api.c b/tools/external_control_client/lib/renode_api.c
index 36f8be1..372f8b0 100644
--- a/tools/external_control_client/lib/renode_api.c
+++ b/tools/external_control_client/lib/renode_api.c
@@ -29,6 +29,11 @@
int32_t md;
};
+struct renode_adc {
+ renode_machine_t *machine;
+ int32_t id;
+};
+
#define SERVER_START_COMMAND "emulation CreateExternalControlServer \"<NAME>\""
#define SOCKET_INVALID -1
@@ -127,6 +132,7 @@
RUN_FOR = 1,
GET_TIME,
GET_MACHINE,
+ ADC,
} api_command_t;
static uint8_t command_versions[][2] = {
@@ -134,6 +140,7 @@
{ RUN_FOR, 0x0 },
{ GET_TIME, 0x0 },
{ GET_MACHINE, 0x0 },
+ { ADC, 0x0 },
};
static renode_error_t *write_or_fail(int socket_fd, const uint8_t *data, ssize_t count)
@@ -415,6 +422,28 @@
return NO_ERROR;
}
+static renode_error_t *renode_get_instance_descriptor(renode_machine_t *machine, api_command_t api_command, const char *name, int32_t *instance_descriptor)
+{
+ uint32_t name_length = strlen(name);
+ uint32_t data_size = name_length + sizeof(int32_t) * 3;
+ int32_t *data __attribute__ ((__cleanup__(xcleanup))) = xmalloc(data_size);
+
+ data[0] = -1;
+ data[1] = machine->md;
+ data[2] = name_length;
+ memcpy(data + 3, name, name_length);
+
+ return_error_if_fails(renode_execute_command(machine->renode, api_command, data, data_size, data_size, &data_size));
+
+ assert_msg(data_size == 4, "received unexpected number of bytes");
+
+ *instance_descriptor = data[0];
+
+ assert_msg(*instance_descriptor >= 0, "received invalid instance descriptor");
+
+ return NO_ERROR;
+}
+
struct run_for_out {
uint64_t microseconds;
};
@@ -463,3 +492,99 @@
return NO_ERROR;
}
+
+renode_error_t *renode_get_adc(renode_machine_t *machine, const char *name, renode_adc_t **adc)
+{
+ int32_t id;
+ return_error_if_fails(renode_get_instance_descriptor(machine, ADC, name, &id));
+
+ *adc = xmalloc(sizeof(renode_adc_t));
+ (*adc)->machine = machine;
+ (*adc)->id = id;
+
+ return NO_ERROR;
+}
+
+typedef enum {
+ GET_CHANNEL_COUNT = 0,
+ GET_CHANNEL_VALUE,
+ SET_CHANNEL_VALUE,
+} adc_command_t;
+
+typedef union {
+ struct {
+ int32_t id;
+ int8_t command;
+ int32_t channel;
+ uint32_t value;
+ } __attribute__((packed)) out;
+
+ struct {
+ int32_t count;
+ } get_count_result;
+
+ struct {
+ uint32_t value;
+ } get_value_result;
+} adc_frame_t;
+
+renode_error_t *renode_get_adc_channel_count(renode_adc_t *adc, int32_t *count)
+{
+ // adc id, adc command -> count
+ adc_frame_t frame = {
+ .out = {
+ .id = adc->id,
+ .command = GET_CHANNEL_COUNT,
+ },
+ };
+
+ uint32_t response_size;
+ return_error_if_fails(renode_execute_command(adc->machine->renode, ADC, &frame, sizeof(frame), offsetof(adc_frame_t, out.channel), &response_size));
+
+ assert_msg(response_size == sizeof(*count), "Received unexpected number of bytes");
+
+ *count = frame.get_count_result.count;
+
+ return NO_ERROR;
+}
+
+renode_error_t *renode_get_adc_channel_value(renode_adc_t *adc, int32_t channel, uint32_t *value)
+{
+ // adc id, adc command, channel index -> value
+ adc_frame_t frame = {
+ .out = {
+ .id = adc->id,
+ .command = GET_CHANNEL_VALUE,
+ .channel = channel,
+ },
+ };
+
+ uint32_t response_size;
+ return_error_if_fails(renode_execute_command(adc->machine->renode, ADC, &frame, sizeof(frame), offsetof(adc_frame_t, out.value), &response_size));
+
+ assert_msg(response_size == sizeof(*value), "Received unexpected number of bytes");
+
+ *value = frame.get_value_result.value;
+
+ return NO_ERROR;
+}
+
+renode_error_t *renode_set_adc_channel_value(renode_adc_t *adc, int32_t channel, uint32_t value)
+{
+ // adc id, adc command, channel index, value -> ()
+ adc_frame_t frame = {
+ .out = {
+ .id = adc->id,
+ .command = SET_CHANNEL_VALUE,
+ .channel = channel,
+ .value = value,
+ },
+ };
+
+ uint32_t response_size;
+ return_error_if_fails(renode_execute_command(adc->machine->renode, ADC, &frame, sizeof(frame), sizeof(frame.out), &response_size));
+
+ assert_msg(response_size == 0, "Received unexpected number of bytes");
+
+ return NO_ERROR;
+}