annotate scripts/run-freq.bmark @ 53:2991344775f8 pygar svn.54

[svn r54] mixer integrated
author punk
date Sun, 09 May 2010 10:58:40 -0400
parents 90197e3375e2
children
rev   line source
rlm@23 1 #!/usr/bin/env perl
rlm@23 2 # -*- perl -*-
rlm@23 3
rlm@23 4 use strict;
rlm@23 5 use warnings;
rlm@23 6 use Getopt::Long qw(:config no_auto_abbrev no_ignore_case pass_through);
rlm@23 7 use IO::Pty;
rlm@23 8
rlm@23 9 #
rlm@23 10 # Turn on warnings
rlm@23 11 #
rlm@23 12 $^W = 1;
rlm@23 13
rlm@23 14 ##
rlm@23 15 ## Benchmark run script
rlm@23 16 ##
rlm@23 17
rlm@23 18 sub ExecModel($);
rlm@23 19 sub Exec($$);
rlm@23 20 sub CompareOutput();
rlm@23 21 sub ReadConfig($$);
rlm@23 22
rlm@23 23
rlm@23 24 #############################################################################
rlm@23 25 #############################################################################
rlm@23 26 ##
rlm@23 27 ## Start by figuring out the model location and type.
rlm@23 28 ##
rlm@23 29 #############################################################################
rlm@23 30 #############################################################################
rlm@23 31
rlm@23 32 my %config;
rlm@23 33
rlm@23 34 ReadConfig("config/env.sh", 1);
rlm@23 35 ReadConfig("$config{modelDir}/config/env.sh", 1);
rlm@23 36 ReadConfig("$config{modelDir}/config/signature.sh", 0);
rlm@23 37
rlm@23 38 ##
rlm@23 39 ## Pseudo-enumeration of possible model types
rlm@23 40 ##
rlm@23 41 my $MODEL_NONE = 0;
rlm@23 42 my $MODEL_FPGA = 1; # Model runs on FPGA hardware
rlm@23 43 my $MODEL_BLUESIM = 2; # Bluesim
rlm@23 44 my $MODEL_VSIM = 3; # Verilog simulator
rlm@23 45
rlm@23 46 my $mType = $MODEL_NONE;
rlm@23 47
rlm@23 48
rlm@23 49 #############################################################################
rlm@23 50 #############################################################################
rlm@23 51 ##
rlm@23 52 ## Process command line arguments
rlm@23 53 ##
rlm@23 54 #############################################################################
rlm@23 55 #############################################################################
rlm@23 56
rlm@23 57 my $help = 0;
rlm@23 58 my $onlyCompare = 0;
rlm@23 59 my $noCompare = 0;
rlm@23 60 my $forceLoad = 0;
rlm@23 61 my $gdb = 0;
rlm@23 62 my $noProgram = 0;
rlm@23 63 my $noReserve = 0;
rlm@23 64 my $funcpPrefix = "";
rlm@23 65 my $funcpSuffix = "";
rlm@23 66 my $noshowfp = 0;
rlm@23 67 my $printCycle = undef;
rlm@23 68 my $showfp = undef;
rlm@23 69 my $bluesimCmd = undef;
rlm@23 70 my $vcdStart = undef;
rlm@23 71 my $vcdCycles = 20000;
rlm@23 72 my $m5run = 0;
rlm@23 73
rlm@23 74 my $status = GetOptions("help!" => \$help,
rlm@23 75 "gdb!" => \$gdb,
rlm@23 76 "noprogram!" => \$noProgram,
rlm@23 77 "noreserve!" => \$noReserve,
rlm@23 78 "force-load!" => \$forceLoad,
rlm@23 79 "funcp-prefix=s" => \$funcpPrefix,
rlm@23 80 "funcp-suffix=s" => \$funcpSuffix,
rlm@23 81 "noshowfp!" => \$noshowfp,
rlm@23 82 "onlycompare!" => \$onlyCompare,
rlm@23 83 "nocompare!" => \$noCompare,
rlm@23 84 "pc=s" => \$printCycle,
rlm@23 85 "showfp:s" => \$showfp,
rlm@23 86 "bluesim=s" => \$bluesimCmd,
rlm@23 87 "vcdstart=i" => \$vcdStart,
rlm@23 88 "vcdcycles=i" => \$vcdCycles,
rlm@23 89 "m5!" => \$m5run,
rlm@23 90 );
rlm@23 91
rlm@23 92 # Put quotation marks back on arguments that have spaces since they will be
rlm@23 93 # passed through a shell once more.
rlm@23 94 foreach my $i ( 0 .. $#ARGV ) {
rlm@23 95 if (($ARGV[$i] =~ /\s/) && ! ($ARGV[$i] =~ /['"]$/)) {
rlm@23 96 $ARGV[$i] = '"' . $ARGV[$i] . '"';
rlm@23 97 }
rlm@23 98 }
rlm@23 99
rlm@23 100 if ($onlyCompare != 0) {
rlm@23 101 exit(CompareOutput());
rlm@23 102 }
rlm@23 103
rlm@23 104 if ($m5run != 0) {
rlm@23 105 $mType = $MODEL_NONE;
rlm@23 106 }
rlm@23 107 elsif (-f "$config{modelDir}/$config{model}_hw.errinfo") {
rlm@23 108 $mType = $MODEL_FPGA;
rlm@23 109 }
rlm@23 110 elsif (-f "$config{modelDir}/$config{model}_hw.exe") {
rlm@23 111 $mType = $MODEL_BLUESIM;
rlm@23 112 }
rlm@23 113 elsif (-f "$config{modelDir}/$config{model}_hw.vexe") {
rlm@23 114 $mType = $MODEL_VSIM;
rlm@23 115 }
rlm@23 116 else {
rlm@23 117 die("Can't determine model type");
rlm@23 118 }
rlm@23 119
rlm@23 120 if ($help || ! $status) {
rlm@23 121 print STDERR "\nArguments:\n";
rlm@23 122 print STDERR " [--gdb] Invokes the software side in gdb\n";
rlm@23 123 print STDERR " [--noprogram] Skips the FPGA load and reservation steps\n";
rlm@23 124 print STDERR " [--noreserve] Skips the FPGA reservation steps\n";
rlm@23 125 print STDERR " [--funcp-prefix=\"<prefix>\"]\n";
rlm@23 126 print STDERR " Prepend prefix to HAsim's --funcp argument\n";
rlm@23 127 print STDERR " [--funcp-suffix=\"<suffix>\"]\n";
rlm@23 128 print STDERR " Append suffix to HAsim's --funcp argument\n";
rlm@23 129 print STDERR " [--onlycompare] Only compare output files (without running)\n";
rlm@23 130 print STDERR " [--nocompare] Skip comparison of output files\n";
rlm@23 131 print STDERR " [--m5] Run workload in m5 without HAsim\n";
rlm@23 132
rlm@23 133 if ($mType == $MODEL_FPGA) {
rlm@23 134 print STDERR " [--force-load] Load a bitfile to the FPGA even if it has errors\n";
rlm@23 135 }
rlm@23 136
rlm@23 137 if ($mType == $MODEL_BLUESIM) {
rlm@23 138 print STDERR " [--vcdstart=<cycle>] Generate VCD dump for wave viewer (e.g. gtkwave)\n";
rlm@23 139 print STDERR " [--vcdcycles=<cycles>] VCD dump length (default = 20000)\n";
rlm@23 140 }
rlm@23 141
rlm@23 142 if ($config{isHybridModel}) {
rlm@23 143 my $cmd = "$config{modelDir}/$config{model} --help-run-append";
rlm@23 144 system($cmd);
rlm@23 145 }
rlm@23 146
rlm@23 147 exit(1);
rlm@23 148 }
rlm@23 149
rlm@23 150 #############################################################################
rlm@23 151 #############################################################################
rlm@23 152 ##
rlm@23 153 ## Adjust model arguments
rlm@23 154 ##
rlm@23 155 #############################################################################
rlm@23 156 #############################################################################
rlm@23 157
rlm@23 158 # Show front panel?
rlm@23 159 if ($noshowfp) {
rlm@23 160 $showfp = 'none';
rlm@23 161 }
rlm@23 162 elsif (defined($showfp)) {
rlm@23 163 # Specified on the run command line
rlm@23 164 $showfp = 'gui' if ($showfp eq '');
rlm@23 165 }
rlm@23 166 elsif ($config{feeder} eq 'none') {
rlm@23 167 # For null feeder default to showing LEDs on stdout
rlm@23 168 $showfp = 'stdout';
rlm@23 169 }
rlm@23 170 else {
rlm@23 171 # Other models have heartbeats
rlm@23 172 $showfp = 'none';
rlm@23 173 }
rlm@23 174
rlm@23 175 my $feedFlags = "${funcpPrefix} $config{feedFlags} ${funcpSuffix}";
rlm@23 176
rlm@23 177 if ($config{feeder} eq 'm5') {
rlm@23 178 # Tell m5 to be quiet and not to enable the remote gdb port. Under netbatch
rlm@23 179 # it appears there are sometimes attempts to connect to the port, which
rlm@23 180 # stops simulation.
rlm@23 181 $feedFlags = "--quiet --remote-gdb-port=0 ${feedFlags}";
rlm@23 182 }
rlm@23 183
rlm@23 184 my $cmd;
rlm@23 185
rlm@23 186 if ($m5run == 0) {
rlm@23 187 # Normal run
rlm@23 188 $cmd = "$config{modelDir}/$config{model} --modeldir=$config{modelDir} --workload=$config{workload} --showfp=${showfp} --funcp=\"${feedFlags}\" $config{genFlags}";
rlm@23 189 foreach my $c (@ARGV) {
rlm@23 190 $cmd .= " $c";
rlm@23 191 }
rlm@23 192
rlm@23 193 if (defined($printCycle)) {
rlm@23 194 $cmd .= " --pc=${printCycle}";
rlm@23 195 }
rlm@23 196 }
rlm@23 197 else {
rlm@23 198 if ($config{feeder} ne 'm5') {
rlm@23 199 die("This workload does not use m5");
rlm@23 200 }
rlm@23 201
rlm@23 202 # Running inside m5 without HAsim
rlm@23 203 my $m5cmd;
rlm@23 204 if (exists($ENV{M5BIN})) {
rlm@23 205 $m5cmd = $ENV{M5BIN};
rlm@23 206 }
rlm@23 207 else {
rlm@23 208 my $m5bin = "platform/m5/build/ALPHA_SE/m5." . ($gdb ? "debug" : "opt");
rlm@23 209 $m5cmd = `awb-resolver ${m5bin}`;
rlm@23 210 chomp($m5cmd);
rlm@23 211 die("Failed to find $m5bin") if ($m5cmd eq '');
rlm@23 212 }
rlm@23 213
rlm@23 214 $cmd = "${m5cmd} ${feedFlags}";
rlm@23 215
rlm@23 216 # Drop --hasim-sim
rlm@23 217 $cmd =~ s/--hasim-sim //;
rlm@23 218 # Drop escaping of quotes
rlm@23 219 $cmd =~ s/\\"/"/g;
rlm@23 220
rlm@23 221 $noProgram = 1;
rlm@23 222 }
rlm@23 223
rlm@23 224 #
rlm@23 225 # Bluesim arguments
rlm@23 226 #
rlm@23 227
rlm@23 228 # Generate dump.vcd for wave viewer (e.g. gtkwave)?
rlm@23 229 if (defined($vcdStart)) {
rlm@23 230 my $vcdCmd = "sim vcd on; sim step $vcdCycles; sim stop";
rlm@23 231 if ($vcdStart > 0) {
rlm@23 232 $vcdCmd = "sim step ${vcdStart}; ${vcdCmd}";
rlm@23 233 }
rlm@23 234
rlm@23 235 if (defined($bluesimCmd)) {
rlm@23 236 $bluesimCmd .= " ";
rlm@23 237 }
rlm@23 238 else {
rlm@23 239 $bluesimCmd = "";
rlm@23 240 }
rlm@23 241 $bluesimCmd .= "-c \"$vcdCmd\"";
rlm@23 242 }
rlm@23 243
rlm@23 244 if (defined($bluesimCmd)) {
rlm@23 245 $cmd .= " --bluesim=\'$bluesimCmd\'";
rlm@23 246 }
rlm@23 247
rlm@23 248 # Adjust the arguments for Bluesim if it is being invoked directly
rlm@23 249 if (! $config{isHybridModel} && ($mType == $MODEL_BLUESIM)) {
rlm@23 250 $cmd =~ s/\s--/ +--/g;
rlm@23 251
rlm@23 252 # Bluesim may expect to load a program from a well known file
rlm@23 253 unlink('program.vmh');
rlm@23 254 link("program/$config{workload}.$config{ISA}.vmh", 'program.vmh');
rlm@23 255 }
rlm@23 256
rlm@23 257
rlm@23 258 #############################################################################
rlm@23 259 #############################################################################
rlm@23 260 ##
rlm@23 261 ## Load the FPGA and run the model
rlm@23 262 ##
rlm@23 263 #############################################################################
rlm@23 264 #############################################################################
rlm@23 265
rlm@23 266 #
rlm@23 267 # Move old stats file so we are sure statistics come from this run
rlm@23 268 #
rlm@23 269 if (-f "$config{workload}.stats") {
rlm@23 270 rename("$config{workload}.stats", "$config{workload}.stats.old");
rlm@23 271 }
rlm@23 272
rlm@23 273 if ($mType == $MODEL_FPGA) {
rlm@23 274 if (! defined($printCycle)) {
rlm@23 275 # User didn't specify a cycle printing interval. Pick one more reasonable
rlm@23 276 # for HW.
rlm@23 277 #$cmd .= " --pc=10000000";
rlm@23 278 }
rlm@23 279
rlm@23 280 # Load FPGA
rlm@23 281 $ENV{FPGA_BIT_FILE} = "$config{modelDir}/.xilinx/$config{model}_par.bit";
rlm@23 282
rlm@23 283 if (! $noProgram) {
rlm@23 284 if (! $forceLoad && -s "$config{modelDir}/$config{model}_hw.errinfo") {
rlm@23 285 print STDERR "FPGA bit file has errors:\n\n";
rlm@23 286 system("cat $config{modelDir}/$config{model}_hw.errinfo > /dev/stderr");
rlm@23 287 print STDERR "\nUse --force-load to ignore the error.\n";
rlm@23 288 exit(1);
rlm@23 289 }
rlm@23 290
rlm@23 291 if (! $noReserve) {
rlm@23 292 Exec("hasim-fpga-ctrl --reserve", "Failed to reserve FPGA");
rlm@23 293 }
rlm@23 294
rlm@23 295 # Does a download script exist to program the FPGA?
rlm@23 296 my $needProgram = (-f "$config{modelDir}/config/$config{model}.download");
rlm@23 297
rlm@23 298 $needProgram = 1;
rlm@23 299
rlm@23 300 # Is the FPGA already programmed with the correct bit file?
rlm@23 301 if (exists($config{signature})) {
rlm@23 302 my $curSignature = `hasim-fpga-ctrl --getsignature`;
rlm@23 303 chomp($curSignature);
rlm@23 304 if ($curSignature eq $config{signature}) {
rlm@23 305 print "FPGA is already programmed (signature match)...\n";
rlm@23 306 #$needProgram = 0;
rlm@23 307 }
rlm@23 308 }
rlm@23 309
rlm@23 310 if ($needProgram) {
rlm@23 311 Exec("hasim-fpga-ctrl --program", "Failed to enter FPGA programming mode");
rlm@23 312
rlm@23 313 my $dir = `pwd`;
rlm@23 314 chomp($dir);
rlm@23 315 Exec("(cd $config{modelDir}; ./config/$config{model}.download ${dir}/FPGA_programming.log)", "Failed to program FPGA");
rlm@23 316
rlm@23 317 if (exists($config{signature})) {
rlm@23 318 Exec("hasim-fpga-ctrl --setsignature=$config{signature}", "Failed to set FPGA bit image signature");
rlm@23 319 }
rlm@23 320 }
rlm@23 321
rlm@23 322 Exec("hasim-fpga-ctrl --activate", "Failed to activate FPGA or driver");
rlm@23 323 }
rlm@23 324 }
rlm@23 325
rlm@23 326 # Run the software side or a hardware simulator
rlm@23 327 my $run_status = 0;
rlm@23 328 if ($config{isHybridModel} || ($mType != $MODEL_FPGA)) {
rlm@23 329 $run_status = ExecModel($cmd);
rlm@23 330 }
rlm@23 331
rlm@23 332 # Create a stats file for null workloads to make regression.launcher happy (HACK)
rlm@23 333 if ( $config{workload} eq "null" ) {
rlm@23 334 system("touch null.stats");
rlm@23 335 }
rlm@23 336
rlm@23 337 if (-f "hasim_events.out") {
rlm@23 338 system("sort hasim_events.out -o hasim_events.out.$$; mv -f hasim_events.out.$$ hasim_events.out");
rlm@23 339 }
rlm@23 340 if (-f "$config{workload}.stats") {
rlm@23 341 system("sort $config{workload}.stats -o $config{workload}.stats.$$; mv -f $config{workload}.stats.$$ $config{workload}.stats");
rlm@23 342 }
rlm@23 343
rlm@23 344 if (($mType == $MODEL_FPGA) && ! $noProgram && ! $noReserve) {
rlm@23 345 Exec("hasim-fpga-ctrl --drop-reservation", "Failed to drop FPGA reservation");
rlm@23 346 }
rlm@23 347
rlm@23 348 if ($run_status != 0) {
rlm@23 349 exit($run_status);
rlm@23 350 }
rlm@23 351 else {
rlm@23 352 exit(CompareOutput());
rlm@23 353 }
rlm@23 354
rlm@23 355
rlm@23 356 sub ErrorExit($) {
rlm@23 357 my $msg = shift;
rlm@23 358
rlm@23 359 print STDERR "${msg}\n";
rlm@23 360
rlm@23 361 if (($mType == $MODEL_FPGA) && ! $noProgram && ! $noReserve) {
rlm@23 362 system("hasim-fpga-ctrl --drop-reservation");
rlm@23 363 }
rlm@23 364
rlm@23 365 exit(1);
rlm@23 366 }
rlm@23 367
rlm@23 368
rlm@23 369 ##
rlm@23 370 ## ExecModel --
rlm@23 371 ## This is the routine that actually invokes the model. stdout and stderr
rlm@23 372 ## are logged in a file. The return value is the exit status of the model.
rlm@23 373 ##
rlm@23 374 sub ExecModel($) {
rlm@23 375 my $cmd = shift;
rlm@23 376
rlm@23 377 if ($gdb) {
rlm@23 378 ## gdb needs stdin. Just use system() and don't do logging.
rlm@23 379 system("gdb -args " . $cmd);
rlm@23 380 return 0;
rlm@23 381 }
rlm@23 382
rlm@23 383 ##
rlm@23 384 ## Invoke the model, but log its output both to stdout and to a file.
rlm@23 385 ## Use a pty so the invoked program will use line buffering instead
rlm@23 386 ## of fully buffered writes. (Libc sets up stdout line buffered when
rlm@23 387 ## it thinks it is writing to a terminal. It uses fully buffered
rlm@23 388 ## writing to a pipe.)
rlm@23 389 ##
rlm@23 390
rlm@23 391 my $pty = new IO::Pty;
rlm@23 392 my $slave = $pty->slave();
rlm@23 393
rlm@23 394 my $pid = fork();
rlm@23 395 die "Couldn't fork: $!" unless defined $pid;
rlm@23 396
rlm@23 397 if (! $pid) {
rlm@23 398 # Child process is the monitoring process
rlm@23 399 $pty->make_slave_controlling_terminal();
rlm@23 400
rlm@23 401 my $output = "$config{workload}.$config{ISA}.out";
rlm@23 402 if(exists($config{silent})) {
rlm@23 403 $output = "/dev/null";
rlm@23 404 }
rlm@23 405
rlm@23 406 if (! open(LOG, ">$output")) {
rlm@23 407 print STDERR "Error opening log file $output\n";
rlm@23 408 }
rlm@23 409
rlm@23 410 # Unbuffered I/O loop
rlm@23 411 while (1) {
rlm@23 412 my $buf;
rlm@23 413 my $n = sysread($slave, $buf, 4096);
rlm@23 414
rlm@23 415 last if ($n == 0);
rlm@23 416
rlm@23 417 syswrite(STDOUT, $buf);
rlm@23 418 syswrite(LOG, $buf);
rlm@23 419 }
rlm@23 420
rlm@23 421 close(LOG);
rlm@23 422 exit(0);
rlm@23 423 }
rlm@23 424
rlm@23 425 # Bind new PTY to STDOUT (but save old STDOUT)
rlm@23 426 $pty->close_slave();
rlm@23 427 open(my $oldOut, ">&", STDOUT) or die $!;
rlm@23 428 open(STDOUT, ">&", $pty) or die $!;
rlm@23 429
rlm@23 430 # Run model
rlm@23 431 my $result = system("${cmd} 2>&1");
rlm@23 432
rlm@23 433 # Send ^d to end child logging thread
rlm@23 434 print "\cD";
rlm@23 435
rlm@23 436 # Return to normal STDOUT
rlm@23 437 close(STDOUT);
rlm@23 438 open(STDOUT, ">&", $oldOut) or die $!;
rlm@23 439 close($oldOut);
rlm@23 440
rlm@23 441 # Compute exit status of model
rlm@23 442 my $status = 0;
rlm@23 443 if ($result == -1) {
rlm@23 444 print STDERR "Model execution failed\n";
rlm@23 445 $status = 1;
rlm@23 446 }
rlm@23 447 elsif ($result & 127) {
rlm@23 448 print STDERR "Child died with signal " . ($result & 127) . ", " . (($result & 128) ? 'with' : 'without') . " coredump\n";
rlm@23 449 $status = 1;
rlm@23 450 }
rlm@23 451 elsif (($result >> 8) != 0) {
rlm@23 452 $status = $result >> 8;
rlm@23 453 print "Model exited with status $status\n";
rlm@23 454 }
rlm@23 455
rlm@23 456 return $status;
rlm@23 457 }
rlm@23 458
rlm@23 459
rlm@23 460 sub Exec($$) {
rlm@23 461 my $cmd = shift;
rlm@23 462 my $errmsg = shift;
rlm@23 463
rlm@23 464 system($cmd);
rlm@23 465 if ($? == -1) {
rlm@23 466 ErrorExit("Failed to execute $cmd: $!");
rlm@23 467 }
rlm@23 468 elsif ($? & 127) {
rlm@23 469 ErrorExit("Child died with signal " . ($? & 127) . ", " . (($? & 128) ? 'with' : 'without') . " coredump");
rlm@23 470 }
rlm@23 471 elsif (($? >> 8) != 0) {
rlm@23 472 ErrorExit("${errmsg}");
rlm@23 473 }
rlm@23 474 }
rlm@23 475
rlm@23 476
rlm@23 477 sub CompareOutput() {
rlm@23 478 return 0 if ($noCompare != 0);
rlm@23 479 return 0 if (! exists($config{compare}) || ($config{compare} eq ''));
rlm@23 480
rlm@23 481 # run the checker
rlm@23 482 `cd ./checker/ && make clean && make`;
rlm@23 483 `./checker/checker input.wav out_gold.wav`;
rlm@23 484 print "about to call compare_wavs_freq\n";
rlm@23 485 my $out=`./checker/compare_wavs/compare_wavs_freq 1 out_gold.wav out_hw.wav`;
rlm@23 486
rlm@23 487 if ($out !~ /fail/) {
rlm@23 488 print "*** Output comparison passed ***\n";
rlm@23 489 system("touch $config{workload}.stats");
rlm@23 490 return 0;
rlm@23 491 }
rlm@23 492 else {
rlm@23 493 print "*** Output comparison failed ***\n";
rlm@23 494 return 1;
rlm@23 495 }
rlm@23 496 }
rlm@23 497
rlm@23 498
rlm@23 499 #
rlm@23 500 # Read the configuration file
rlm@23 501 #
rlm@23 502 sub ReadConfig($$) {
rlm@23 503 my $conf = shift;
rlm@23 504 my $required = shift;
rlm@23 505
rlm@23 506 my $status = open(CONFIG, "< $conf");
rlm@23 507 if (! $status) {
rlm@23 508 return if (! $required);
rlm@23 509 die("Failed to open $conf");
rlm@23 510 }
rlm@23 511
rlm@23 512 while (<CONFIG>) {
rlm@23 513 chomp;
rlm@23 514 my $t = $_;
rlm@23 515 $t =~ s/#.*//;
rlm@23 516 if ($t =~ /^\s*([^\s]+)\s*=\s*"(.*)"\s*$/) {
rlm@23 517 my $c = $1;
rlm@23 518 my $v = $2;
rlm@23 519 $v =~ s/^["'](.*)["']$/$1/; # Drop quotation marks
rlm@23 520 $config{$c} = $v;
rlm@23 521 }
rlm@23 522 elsif ($t =~ /^\s*([^\s]+)\s*=\s*([^\s]+)\s*$/) {
rlm@23 523 my $c = $1;
rlm@23 524 my $v = $2;
rlm@23 525 $config{$c} = $v;
rlm@23 526 }
rlm@23 527 }
rlm@23 528 }