1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under MIT License.
5 // Full license text is available in 'licenses/MIT.txt' file.
6 //
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include "renode_api.h"
14 
exit_with_usage_info(const char * argv0)15 void exit_with_usage_info(const char *argv0)
16 {
17     fprintf(stderr,
18         "Usage:\n"
19         "  %s <PORT> <MACHINE_NAME> <ADC_NAME> <VALUE_WITH_UNIT>\n"
20         "  where:\n"
21         "  * <VALUE_WITH_UNIT> is an unsigned integer with a voltage unit, e.g.: '100mV'\n"
22         "  * accepted voltage units are 'V', 'mV' and 'uV' (for microvolts)\n",
23         argv0);
24     exit(EXIT_FAILURE);
25 }
26 
get_error_message(renode_error_t * error)27 char *get_error_message(renode_error_t *error)
28 {
29     if (error->message == NULL)
30     {
31         return "<no message>";
32     }
33     return error->message;
34 }
35 
try_renode_disconnect(renode_t ** renode)36 int try_renode_disconnect(renode_t **renode)
37 {
38     renode_error_t *error;
39     if ((error = renode_disconnect(renode)) != NO_ERROR) {
40         fprintf(stderr, "Disconnecting from Renode failed with: %s\n", get_error_message(error));
41         return -1;
42     }
43     return 0;
44 }
45 
voltage_string(uint32_t value)46 char *voltage_string(uint32_t value)
47 {
48     static char buffer[32];
49     uint32_t integer, fraction;
50 
51     if ((integer = (value / 1000000))) {
52         fraction = value % 1000000;
53         snprintf(buffer, 32, "%u.%06uV", integer, fraction);
54     }
55     else if ((integer = (value / 1000))) {
56         fraction = value % 1000;
57         snprintf(buffer, 32, "%u.%03umV", integer, fraction);
58     }
59     else {
60         snprintf(buffer, 32, "%uuV", value);
61     }
62 
63     return buffer;
64 }
65 
main(int argc,char ** argv)66 int main(int argc, char **argv)
67 {
68     if (argc != 5) {
69         exit_with_usage_info(argv[0]);
70     }
71     char *machine_name = argv[2];
72     char *adc_name = argv[3];
73 
74     char *endptr;
75     // base=0 tries to figure out the number's base automatically.
76     uint64_t value = strtoul(argv[4], &endptr, /* base: */ 0);
77     if (errno != 0) {
78         perror("conversion to uint32_t value");
79         exit(EXIT_FAILURE);
80     }
81 
82     if (endptr == argv[4]) {
83         exit_with_usage_info(argv[0]);
84     }
85 
86     switch (*endptr) {
87         case 'u':
88             // the unit used with the API
89             break;
90         case 'm':
91             value *= 1000;
92             break;
93         case 'V':
94             value *= 1000000;
95             break;
96         default:
97             exit_with_usage_info(argv[0]);
98     }
99 
100     if (value > UINT32_MAX) {
101         fprintf(stderr, "Voltage value too big\n");
102         exit(EXIT_FAILURE);
103     }
104 
105     // get Renode, machine and ADC instances
106 
107     renode_error_t *error;
108     renode_t *renode;
109     if ((error = renode_connect(argv[1], &renode)) != NO_ERROR) {
110         fprintf(stderr, "Connecting to Renode failed with: %s\n", get_error_message(error));
111         goto fail;
112     }
113 
114     renode_machine_t *machine;
115     if ((error = renode_get_machine(renode, machine_name, &machine)) != NO_ERROR) {
116         fprintf(stderr, "Getting '%s' machine object failed with: %s\n", machine_name, get_error_message(error));
117         goto fail_renode;
118     }
119 
120     renode_adc_t *adc;
121     if ((error = renode_get_adc(machine, adc_name, &adc)) != NO_ERROR) {
122         fprintf(stderr, "Getting '%s' ADC object failed with: %s\n", adc_name, get_error_message(error));
123         goto fail_machine;
124     }
125 
126     // assert that at least one channel exists
127 
128     int32_t ch_count;
129     if ((error = renode_get_adc_channel_count(adc, &ch_count)) != NO_ERROR) {
130         fprintf(stderr, "Getting channel count for '%s' failed with: %s\n", adc_name, get_error_message(error));
131         goto fail_adc;
132     }
133 
134     if (ch_count < 1) {
135         fprintf(stderr, "Expected at least one ADC channel\n");
136         goto fail_adc;
137     }
138     printf("[INFO] # of channels: %d\n", ch_count);
139 
140     // get current value, set the new value and assert that new current value is set
141 
142     uint32_t val0;
143     if ((error = renode_get_adc_channel_value(adc, 0, &val0)) != NO_ERROR) {
144         fprintf(stderr, "Getting channel #0 value for '%s' failed with: %s\n", adc_name, get_error_message(error));
145         goto fail_adc;
146     }
147 
148     printf("ADC value: %s\n", voltage_string(val0));
149 
150     uint32_t val1 = value;
151     if ((error = renode_set_adc_channel_value(adc, 0, val1)) != NO_ERROR) {
152         fprintf(stderr, "Setting channel #0 value for '%s' failed with: %s\n", adc_name, get_error_message(error));
153         goto fail_adc;
154     }
155 
156     printf("ADC value set to %s\n", voltage_string(val1));
157 
158     uint32_t val2;
159     if ((error = renode_get_adc_channel_value(adc, 0, &val2)) != NO_ERROR) {
160         fprintf(stderr, "Getting channel #0 value for '%s' failed with: %s\n", adc_name, get_error_message(error));
161         goto fail_adc;
162     }
163 
164     printf("ADC value: %s\n", voltage_string(val2));
165 
166     // clean up
167 
168     free(adc);
169     free(machine);
170     if (try_renode_disconnect(&renode)) {
171         exit(EXIT_FAILURE);
172     }
173 
174     if (val1 != val2) {
175         fprintf(stderr, "ADC value doesn't match set value\n");
176         exit(EXIT_FAILURE);
177     }
178 
179     exit(EXIT_SUCCESS);
180 
181 fail_adc:
182     free(adc);
183 fail_machine:
184     free(machine);
185 fail_renode:
186     try_renode_disconnect(&renode);
187     free(renode);
188 fail:
189     renode_free_error(error);
190     exit(EXIT_FAILURE);
191 }
192