Settings.pm 9.1 KB
Newer Older
1
## @file
2

3
## @class FIJI::Settings
4
#
5
# Contains helper functions to deal with FIJI Settings files.
6
7
package FIJI::Settings;

8
9
10
use strict;
use warnings;

11
12
13
14
use Log::Log4perl qw(get_logger);
use Scalar::Util "looks_like_number";
use Config::Simple;

15
16
use FIJI qw(:all);

17
18
## @function read_configfile ($fiji_ini_file)
# @brief Load the FIJI Settings file containing design and FIU constants.
19
#
20
21
22
23
24
25
# \param fiji_ini_file The name of an .ini file with FIJI Settings:
#         - a 'consts' block containing the constants specified by
#           \ref _sanitize_consts.
#         - at least one FIU block named "FIU<number>" where "<number>"
#           is a strictly increasing integer starting with 0 containing
#           the constants for the respective FIU, see \ref _sanitize_fiu
26
27
28
29
#
# \returns a reference to the hash containing the read constants.
sub read_configfile {
  my $logger = get_logger();
30
31
32
33
34
  my ($fiji_ini_file) = @_;
  my $fiji_ini;
  eval { $fiji_ini = new Config::Simple($fiji_ini_file) }; # pesky library tries to die on syntax errors
  if (!defined($fiji_ini)) {
    $logger->fatal("Could not read config file \"$fiji_ini_file\": " . (defined($@) ? $@ : Config::Simple->error()));
35
36
37
    return undef;
  }

38
  my $fiji_consts = $fiji_ini->get_block("CONSTS");
39
  if (!(%$fiji_consts)) {
40
    $logger->fatal("Could not fetch CONSTS block from config file \"$fiji_ini_file\"");
41
42
    return undef;
  }
43
44
  $fiji_consts = _sanitize_consts($fiji_consts);
  if (!defined($fiji_consts)) {
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
    $logger->fatal("Design constants in FIJI Settings invalid");
    return undef;
  }

  my $fiu_num = 0;
  while (1) {
    my $fiu_name = "FIU" . $fiu_num;
    my $fiji_fiu_cfg = $fiji_ini->get_block($fiu_name);
    if (!(%$fiji_fiu_cfg)) {
      last;
    }
    my $fiji_fiu = _sanitize_fiu($fiji_fiu_cfg);
    if (!defined($fiji_fiu)) {
      $logger->fatal("Constants for $fiu_name in FIJI Settings are invalid");
      return undef;
    }
    push(@{$fiji_consts->{'FIUs'}}, $fiji_fiu);
    $fiu_num++;
    $logger->trace("Read in $fiu_name from FIJI Settings file successfully.");
  }

  if ($fiu_num == 0) {
    $logger->fatal("Could not fetch any FIU block from config file \"$fiji_ini_file\"");
    return undef;
69
  }
70
71
72
73
74
75
76
77
78
79
80

  # FIU_NUM is optional in the Settings file... if it was set check that
  # it corresponds to the number of FIU<number> blocks.
  if (defined($fiji_consts->{'FIU_NUM'}) && $fiji_consts->{'FIU_NUM'} != $fiu_num) {
      $logger->fatal(FIU_NUM->{'ini_name'} . " does not match the numbers of FIU blocks found.");
      return undef;
  } else {
    $fiji_consts->{'FIU_NUM'} = $fiu_num; # assume the best if FIU_NUM constant is not given
  }

  $logger->info("Successfully read in design constants and $fiu_num FIU definitions from FIJI Settings file.");
81
82
83
84
  return $fiji_consts;
}


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
## @function _set_defaults (%$map_ref, %$consts_ref)
# @brief Set defaults according to FIJI.pm.
#
# The functions compares the given constants to the defaults and rules
# in FIJI.pm.
sub _set_defaults {
  my $logger = get_logger();
  my ($map_ref, $consts_ref) = @_;
  my $new_hash = {};

  # Iterating over respective hash from FIJI.pm and set defaults if need be
  foreach my $k (keys($map_ref)) {
    my $ini_name = $map_ref->{$k}->{'ini_name'};
    if (exists($consts_ref->{$ini_name})) {
      $new_hash->{$k} = $consts_ref->{$ini_name};
      $logger->trace(sprintf("Copying setting %s (%s) = %s.", $k, $ini_name, $consts_ref->{$ini_name}));
    } else {
      if (exists($map_ref->{$k}->{default})) {
        $new_hash->{$k} = $map_ref->{$k}->{default};
        $logger->trace(sprintf("Adding default constant: %s (%s) = %s.", $k, $ini_name, $map_ref->{$k}->{default}));
        # If the default key is there but its value is undef then
        # the value will be set somewhere else later (used for FIU_NUM)
        if (!defined($new_hash->{$k})) {
          next;
        }
      } else {
        $logger->error(sprintf("%s is missing from FIJI Settings.", $ini_name));
        return undef;
      }
    }

    # convert non-decimal (hexadecimal, binary, octal) values to decimal
    if ($map_ref->{$k}->{'is_numeric'}) {
118
      my $orig = $new_hash->{$k};
119
120
121
122
123
124
      $new_hash->{$k} = oct($orig) if $orig =~ /^0/;
      $logger->trace("Converted value of $k (\"$orig\") to \"$new_hash->{$k}\".") if ($orig ne $new_hash->{$k});
      if (!looks_like_number($new_hash->{$k})) {
        $logger->error("$orig does not look like a number.");
        return undef;
      }
125
126
127
128
129
    } elsif (defined($map_ref->{$k}->{'values'})) {
      if (!grep {$_ eq $new_hash->{$k}} @{$map_ref->{$k}->{'values'}}) {
        $logger->error("$new_hash->{$k} is not allowed. Allowed values are: " . join(", ", @{$map_ref->{$k}->{'values'}}));
        return undef;
      }
130
131
132
    }
  }
  return $new_hash;
133
134
135
}

## @function _sanitize_fiu (%$fiu_ref)
136
137
# @brief Convert and sanity check FIJI Settings.
#
138
139
140
141
142
143
144
145
146
147
148
149
150
# \param fiu_ref a reference to a hash containing FIJI Settings for a
#                single FIU.
#
# \returns A new hash with all constants required in the FIU settings
#          in sanitized form, or undef on errors.
sub _sanitize_fiu {
  my $logger = get_logger();
  my ($fiu_ref) = @_;
  if (ref($fiu_ref) ne 'HASH') {
    $logger->error("Parameter is not a reference to a hash (containing FIU constants).");
    return undef;
  }

151
152
153
154
  my $new_consts = _set_defaults(FIUMAP, $fiu_ref);
  if (!defined($new_consts)) {
    $logger->fatal("Could not set defaults for design constants.");
    return undef;
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  }

  # check for sane values
  # if (($new_hash->{FIU_CFG_BITS} <= 0)) {
      # $logger->error("FIU_CFG_BITS is <= 0.");
      # return undef;
  # }

  return $fiu_ref;
}

## @function _sanitize_consts (%$consts_ref)
# @brief Convert and sanity check FIJI Settings for the whole design.
#
# NOTE: The function returns a reference to a *new* hashmap that contains
#       only the design constants!
#
# This function takes a hash of design settings and converts it to the
173
174
175
176
177
178
179
180
# respective internal representation. This allows to use different
# names in the external file than within the implementation.
#
# First, this function sets some default values for missing constants if
# there is a default stored for the respective constant in FIJI.pm.
# If there is no default available it has to be a mandatory value and
# hence the function returns non-0 in that case.
#
181
182
183
184
185
186
# ARM_DURATION_WIDTH and INJECT_DURATION_WIDTH are handled differently
# because they are not official FIJI Settings because there is only a
# single timer (yet). However, the Perl implementation supports
# independent timer widths named as above. If they are not given in
# consts_ref then the value of TIMER_WIDTH is used. If that is not given
# either the default value for TIMER_WIDTH is used.
187
188
189
#
# The second part of the function deals with sanity checks for the values
# themselves. It checks for the following conditions:
190
191
192
#
#   - FIU_NUM: > 0
#   - FIU_CFG_BITS: > 0
193
#   - ARM_DURATION_WIDTH, INJECT_DURATION_WIDTH: > 0, multiple of 8
194
195
196
#   - ID: > 0, < 2^15-1
#   - BAUDRATE: > 0
#
197
# \param consts_ref a reference to a hash containing some design settings
198
#
199
200
# \returns A new hash with all constants required in the design settings
#          in sanitized form, or undef on errors.
201
202
203
204
sub _sanitize_consts {
  my $logger = get_logger();
  my ($consts_ref) = @_;
  if (ref($consts_ref) ne 'HASH') {
205
    $logger->error("Parameter is not a reference to a hash (containing design constants).");
206
207
208
209
210
211
    return undef;
  }

  # Special cases of defaults handled separately here.
  # Set ARM_DURATION_WIDTH and INJECT_DURATION_WIDTH to TIMER_WIDTH (or its default)
  foreach my $k ('ARM_DURATION_WIDTH', 'INJECT_DURATION_WIDTH') {
212
    if (!exists($consts_ref->{DESIGNMAP->{$k}->{'ini_name'}})) {
213
      if (exists($consts_ref->{'TIMER_WIDTH'})) {
214
215
        $consts_ref->{DESIGNMAP->{$k}->{'ini_name'}} = $consts_ref->{'TIMER_WIDTH'};
        $logger->trace(sprintf("Using TIMER_WIDTH value as default for %s (%s).", DESIGNMAP->{$k}->{'ini_name'}, $consts_ref->{'TIMER_WIDTH'}));
216
      } else {
217
218
        $consts_ref->{DESIGNMAP->{$k}->{'ini_name'}} = TIMER_WIDTH->{'default'};
        $logger->trace(sprintf("Using TIMER_WIDTH default as default for %s (%s).", DESIGNMAP->{$k}->{'ini_name'}, TIMER_WIDTH->{default}));
219
220
      }
    }
221
222
  }

223
224
225
226
  my $new_consts = _set_defaults(DESIGNMAP, $consts_ref);
  if (!defined($new_consts)) {
    $logger->fatal("Could not set defaults for design constants.");
    return undef;
227
228
  }

229
230
  # check for sane values
  if (($new_consts->{FIU_CFG_BITS} <= 0)) {
231
      $logger->error("FIU_CFG_BITS is <= 0.");
232
      return undef;
233
  }
234
235
236
  if (($new_consts->{ARM_DURATION_WIDTH} <= 0) || ($new_consts->{ARM_DURATION_WIDTH} % 8 != 0)) {
      $logger->error("ARM_DURATION_WIDTH is invalid ($new_consts->{ARM_DURATION_WIDTH}).");
      return undef;
237
  }
238
239
240
  if (($new_consts->{INJECT_DURATION_WIDTH} <= 0) || (($new_consts->{INJECT_DURATION_WIDTH} % 8) != 0)) {
      $logger->error("INJECT_DURATION_WIDTH is invalid ($new_consts->{INJECT_DURATION_WIDTH}).");
      return undef;
241
  }
242
243
244
  if (($new_consts->{ID} <= 0) || ($new_consts->{ID} > (2**15 - 1))) {
      $logger->error("ID is invalid ($new_consts->{ID}).");
      return undef;
245
  }
246
  if (($new_consts->{BAUDRATE} <= 0)) {
247
      $logger->error("BAUDRATE missing is <= 0.");
248
      return undef;
249
  }
250
  return $new_consts;
251
252
253
}

1;