Settings.pm 9.09 KB
Newer Older
1
2
3
4
## @file [Settings.pm]

## @class [FIJI::Settings]
#
5
# Contains helper functions to deal with FIJI Settings files.
6
7
8
9
10
11
package FIJI::Settings;

use Log::Log4perl qw(get_logger);
use Scalar::Util "looks_like_number";
use Config::Simple;

12
13
use FIJI qw(:all);

14
15
## @function read_configfile ($fiji_ini_file)
# @brief Load the FIJI Settings file containing design and FIU constants.
16
#
17
18
19
20
21
22
# \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
23
24
25
26
#
# \returns a reference to the hash containing the read constants.
sub read_configfile {
  my $logger = get_logger();
27
28
29
30
31
  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()));
32
33
34
    return undef;
  }

35
  my $fiji_consts = $fiji_ini->get_block("CONSTS");
36
  if (!(%$fiji_consts)) {
37
    $logger->fatal("Could not fetch CONSTS block from config file \"$fiji_ini_file\"");
38
39
    return undef;
  }
40
41
  $fiji_consts = _sanitize_consts($fiji_consts);
  if (!defined($fiji_consts)) {
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    $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;
66
  }
67
68
69
70
71
72
73
74
75
76
77

  # 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.");
78
79
80
81
  return $fiji_consts;
}


82
83
84
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
## @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'}) {
115
      my $orig = $new_hash->{$k};
116
117
118
119
120
121
      $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;
      }
122
123
124
125
126
    } 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;
      }
127
128
129
    }
  }
  return $new_hash;
130
131
132
}

## @function _sanitize_fiu (%$fiu_ref)
133
134
# @brief Convert and sanity check FIJI Settings.
#
135
136
137
138
139
140
141
142
143
144
145
146
147
# \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;
  }

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

  # 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
170
171
172
173
174
175
176
177
# 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.
#
178
179
180
181
182
183
# 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.
184
185
186
#
# The second part of the function deals with sanity checks for the values
# themselves. It checks for the following conditions:
187
188
189
#
#   - FIU_NUM: > 0
#   - FIU_CFG_BITS: > 0
190
#   - ARM_DURATION_WIDTH, INJECT_DURATION_WIDTH: > 0, multiple of 8
191
192
193
#   - ID: > 0, < 2^15-1
#   - BAUDRATE: > 0
#
194
# \param consts_ref a reference to a hash containing some design settings
195
#
196
197
# \returns A new hash with all constants required in the design settings
#          in sanitized form, or undef on errors.
198
199
200
201
sub _sanitize_consts {
  my $logger = get_logger();
  my ($consts_ref) = @_;
  if (ref($consts_ref) ne 'HASH') {
202
    $logger->error("Parameter is not a reference to a hash (containing design constants).");
203
204
205
206
207
208
    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') {
209
    if (!exists($consts_ref->{DESIGNMAP->{$k}->{'ini_name'}})) {
210
      if (exists($consts_ref->{'TIMER_WIDTH'})) {
211
212
        $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'}));
213
      } else {
214
215
        $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}));
216
217
      }
    }
218
219
  }

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

226
227
  # check for sane values
  if (($new_consts->{FIU_CFG_BITS} <= 0)) {
228
      $logger->error("FIU_CFG_BITS is <= 0.");
229
      return undef;
230
  }
231
232
233
  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;
234
  }
235
236
237
  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;
238
  }
239
240
241
  if (($new_consts->{ID} <= 0) || ($new_consts->{ID} > (2**15 - 1))) {
      $logger->error("ID is invalid ($new_consts->{ID}).");
      return undef;
242
  }
243
  if (($new_consts->{BAUDRATE} <= 0)) {
244
      $logger->error("BAUDRATE missing is <= 0.");
245
      return undef;
246
  }
247
  return $new_consts;
248
249
250
}

1;