Commit 65415550 authored by Stefan Tauner's avatar Stefan Tauner
Browse files

fiji_scripts: settings revamp

 - get rid of separate ARM and INJECT DURATION widths
 - allow for different external and internal representation of values
 - highlight invalid user entries
 - validation of settings depends on tool flow phase (e.g. setup)
parent 84a8f49e
...@@ -13,58 +13,54 @@ use warnings; ...@@ -13,58 +13,54 @@ use warnings;
# Fields: # Fields:
# - ini_name = key name in FIJI Settings file # - ini_name = key name in FIJI Settings file
# - unit = (optional) physical unit # - unit = (optional) physical unit
# - not_supplied = (optional) not to be set by the user (but generated e.g. by fiji_instrument)
# - type = (optional) enables type-specific conversions and tests: # - type = (optional) enables type-specific conversions and tests:
# numeric: values must be oct, hex, binary strings looking like a real number. # natural: values must be oct, hex, binary strings looking like a real number.
# hexadecimal: values must be hexadecimal numbers.
# boolean: will be convert to a truth value by Perl semantics # boolean: will be convert to a truth value by Perl semantics
# values = (optional) an array reference listing all valid values (emulates an enum) # - values = (optional) an array reference listing all valid values (emulates an enum)
# default = (optional) default value if not given in file and not determinable otherwise # - phases_opt = (optional) list of phases (subset of "setup", "instrument", "download")
# where no value must be present in input (e.g. ID is only necessary while downloading).
# - default = (optional) default value if not given in file and not determinable otherwise
my %designmap; my %designmap;
BEGIN { BEGIN {
%designmap = ( %designmap = (
ID => { ID => {
ini_name => "ID", ini_name => "ID",
type => 'numeric', type => 'hexadecimal',
not_supplied => 1, phases_opt => [qw(setup instrument)], # generated in instrumentation
}, },
FIU_NUM => { FIU_NUM => {
ini_name => "FIU_NUM", ini_name => "FIU_NUM",
type => 'numeric', type => 'natural',
not_supplied => 1, # auto-generated phases_opt => [qw(setup instrument download)], # auto-generated if need be
}, },
BAUDRATE => { BAUDRATE => {
ini_name => "BAUDRATE", ini_name => "BAUDRATE",
default => 115200, default => 115200,
type => 'numeric', type => 'natural',
unit => 'bps', unit => 'bps',
}, },
FREQUENCY => { FREQUENCY => {
ini_name => "FREQUENCY", ini_name => "FREQUENCY",
default => 50e8, default => 50e8,
type => 'numeric', type => 'natural',
unit => 'Hz', unit => 'Hz',
}, },
FIU_CFG_BITS => { FIU_CFG_BITS => {
ini_name => "FIU_CFG_BITS", ini_name => "FIU_CFG_BITS",
default => 3, default => 3,
type => 'numeric', type => 'natural',
not_supplied => 1, # currently not user-configurable phases_opt => [qw(setup instrument download)], # currently not user-configurable at all
}, },
TIMER_WIDTH => { TIMER_WIDTH => {
ini_name => "TIMER_WIDTH", ini_name => "TIMER_WIDTH",
default => 32, default => 32,
type => 'numeric', type => 'natural',
unit => 'bits', unit => 'bits',
}, values => sub {
ARM_DURATION_WIDTH => { my $val = shift;
ini_name => "ARM_DUR_WIDTH", return $val % 8 == 0 && $val > 0 && $val <= 64;
type => 'numeric', }
not_supplied => 1, # derived from TIMER_WIDTH if need be
},
INJECT_DURATION_WIDTH => {
ini_name => "INJECT_DUR_WIDTH",
type => 'numeric',
not_supplied => 1, # derived from TIMER_WIDTH if need be
}, },
); );
} }
...@@ -77,26 +73,31 @@ BEGIN { ...@@ -77,26 +73,31 @@ BEGIN {
%fiumap = ( %fiumap = (
FIU_NET_NAME => { FIU_NET_NAME => {
ini_name => "NET_NAME", ini_name => "NET_NAME",
phases_opt => [qw(setup)], # defaults to undef
}, },
FIU_MODEL => { FIU_MODEL => {
ini_name => "FAULT_MODEL", ini_name => "FAULT_MODEL",
default => "RUNTIME", default => "RUNTIME",
values => [qw(RUNTIME PASS_THRU STUCK_AT_0 STUCK_AT_1 STUCK_OPEN DELAY SEU)], values => [qw(RUNTIME PASS_THRU STUCK_AT_0 STUCK_AT_1 STUCK_OPEN DELAY SEU)],
phases_opt => [qw(setup)],
}, },
FIU_LFSR_EN => { FIU_LFSR_EN => {
ini_name => "ENABLED_BY_LFSR", ini_name => "ENABLED_BY_LFSR",
default => 0, default => 0,
type => 'boolean', type => 'boolean',
phases_opt => [qw(setup)],
}, },
FIU_LFSR_MASK => { FIU_LFSR_MASK => {
ini_name => "LFSR_MASK", ini_name => "LFSR_MASK",
default => 0, default => 0,
type => 'numeric', type => 'hexadecimal',
phases_opt => [qw(setup)],
}, },
FIU_LFSR_STUCK_OPEN_BIT => { FIU_LFSR_STUCK_OPEN_BIT => {
ini_name => "LFSR_BIT_FOR_STUCK_OPEN", ini_name => "LFSR_BIT_FOR_STUCK_OPEN",
default => 0, default => 0,
type => 'numeric', type => 'natural',
phases_opt => [qw(setup)],
}, },
); );
} }
......
...@@ -122,7 +122,7 @@ sub reset_comm { ...@@ -122,7 +122,7 @@ sub reset_comm {
push(@bytes, 0x00); push(@bytes, 0x00);
# Everything else # Everything else
for (my $i = 0; $i < $consts_ref->{'ARM_DURATION_WIDTH'} / 8 + $consts_ref->{'INJECT_DURATION_WIDTH'} / 8 + 2; $i++) { for (my $i = 0; $i < 2 * $consts_ref->{'TIMER_WIDTH'} / 8 + 2; $i++) {
push(@bytes, 0x00); push(@bytes, 0x00);
} }
if (_send_bitstream($port, \pack('C*', @bytes), 1000) != scalar(@bytes)) { if (_send_bitstream($port, \pack('C*', @bytes), 1000) != scalar(@bytes)) {
...@@ -150,7 +150,7 @@ sub reset_comm { ...@@ -150,7 +150,7 @@ sub reset_comm {
# - payload: a byte array representing the FIU configuration # - payload: a byte array representing the FIU configuration
# - arm_duration and inject_duration (optional): # - arm_duration and inject_duration (optional):
# initialization values for the arm and injection duration counters (minus one, actually). # initialization values for the arm and injection duration counters (minus one, actually).
# - consts: a reference to a hash representing FIJI constants (see \ref FIJI::Settings::_sanitize_consts). # - consts: a reference to a hash representing FIJI constants (see \ref FIJI::Settings::_sanitize_design).
# The following values are optional booleans and hence not checked: # The following values are optional booleans and hence not checked:
# - reset # - reset
# - trigger # - trigger
...@@ -204,10 +204,9 @@ sub sanitize_config { ...@@ -204,10 +204,9 @@ sub sanitize_config {
$logger->error(sprintf("Configuration value \"%s\" is negative (%d).", $k, $config_ref->{$k})); $logger->error(sprintf("Configuration value \"%s\" is negative (%d).", $k, $config_ref->{$k}));
return 1; return 1;
} }
my $width_name = uc($k)."_WIDTH"; # availablity must have been checked with FIJI::Settings::_sanitize_consts already my $max_val = 2 ** $consts_ref->{'TIMER_WIDTH'} - 1;
my $max_val = 2 ** $consts_ref->{$width_name} - 1;
if ($config_ref->{$k} > $max_val) { if ($config_ref->{$k} > $max_val) {
$logger->error(sprintf("Configuration value \"%s\" is too big (%d) for %d bits.", $k, $config_ref->{$k}, $consts_ref->{$width_name})); $logger->error(sprintf("Configuration value \"%s\" is too big (%d) for %d bits.", $k, $config_ref->{$k}, $consts_ref->{'TIMER_WIDTH'}));
return 1; return 1;
} }
} }
...@@ -256,7 +255,7 @@ sub send_config { ...@@ -256,7 +255,7 @@ sub send_config {
$arm_duration_en = 1; $arm_duration_en = 1;
} }
my @arm_duration_arr; my @arm_duration_arr;
for (my $i = 0; $i < $consts_ref->{'ARM_DURATION_WIDTH'} / 8; $i++) { for (my $i = 0; $i < $consts_ref->{'TIMER_WIDTH'} / 8; $i++) {
push(@arm_duration_arr, ($arm_duration >> ($i * 8)) & 0xFF); push(@arm_duration_arr, ($arm_duration >> ($i * 8)) & 0xFF);
} }
...@@ -267,7 +266,7 @@ sub send_config { ...@@ -267,7 +266,7 @@ sub send_config {
$inject_duration_en = 1; $inject_duration_en = 1;
} }
my @inject_duration_arr; my @inject_duration_arr;
for (my $i = 0; $i < $consts_ref->{'INJECT_DURATION_WIDTH'} / 8; $i++) { for (my $i = 0; $i < $consts_ref->{'TIMER_WIDTH'} / 8; $i++) {
push(@inject_duration_arr, ($inject_duration >> ($i * 8)) & 0xFF); push(@inject_duration_arr, ($inject_duration >> ($i * 8)) & 0xFF);
} }
......
This diff is collapsed.
...@@ -24,6 +24,7 @@ Construct Tk::Widget 'FIJISettingsViewer'; ...@@ -24,6 +24,7 @@ Construct Tk::Widget 'FIJISettingsViewer';
my $fr_design; # labled frame surrounding widgets representing design constant my $fr_design; # labled frame surrounding widgets representing design constant
my $fr_fius; # labled frame surrounding widgets representing design constant my $fr_fius; # labled frame surrounding widgets representing design constant
my $widget_background;
sub ClassInit { sub ClassInit {
...@@ -34,6 +35,7 @@ sub ClassInit { ...@@ -34,6 +35,7 @@ sub ClassInit {
return $self; return $self;
} }
sub Populate { sub Populate {
my $logger = get_logger(); my $logger = get_logger();
my($self, $args) = @_; my($self, $args) = @_;
...@@ -47,6 +49,7 @@ sub Populate { ...@@ -47,6 +49,7 @@ sub Populate {
${$self->{'settings'}}->{'FIUs'} = []; ${$self->{'settings'}}->{'FIUs'} = [];
} }
} }
# FIXME: add an option to store a CODE reference that is called when any field is invalid
$self->SUPER::Populate($args); $self->SUPER::Populate($args);
$self->_populate_widget($self); $self->_populate_widget($self);
$self->update(); $self->update();
...@@ -64,6 +67,7 @@ sub update { ...@@ -64,6 +67,7 @@ sub update {
# design panel # # design panel #
################ ################
# Ugly hack to retrieve the design constants widgets.
# Alternatively to below one could store the respective widgets separately. # Alternatively to below one could store the respective widgets separately.
# To limit the complexity elsewhere we jump though a few hoops here. # To limit the complexity elsewhere we jump though a few hoops here.
# 1. fetch the widgets comprising the GUI for the design constants # 1. fetch the widgets comprising the GUI for the design constants
...@@ -74,7 +78,8 @@ sub update { ...@@ -74,7 +78,8 @@ sub update {
my @design_widgets = ${${$fr_design->children}[0]->children}[1]->children; my @design_widgets = ${${$fr_design->children}[0]->children}[1]->children;
my $const_cnt = @design_widgets; my $const_cnt = @design_widgets;
for (my $i = 3; $i < $const_cnt; $i += 3) { for (my $i = 3; $i < $const_cnt; $i += 3) {
my ($namew, $unitw, $valw) = @design_widgets[$i..$i+3]; # The order of the widgets depends on their construction time(!)
my ($valw, $namew, $unitw) = @design_widgets[$i..$i+3];
my $name = $namew->cget('-text'); my $name = $namew->cget('-text');
my $k = FIJI::ini2constkey($name); my $k = FIJI::ini2constkey($name);
if (!defined($k)) { if (!defined($k)) {
...@@ -87,7 +92,8 @@ sub update { ...@@ -87,7 +92,8 @@ sub update {
return; return;
} }
$logger->trace("Connect widget ($name) with new settings instance hash ($k)"); $logger->trace("Connect widget ($name) with new settings instance hash ($k)");
$valw->configure('-textvariable' => \${$self->{'settings'}}->{'design'}->{$k}); $valw->{'fiji_backend_ref'} = \${$self->{'settings'}}->{'design'}->{$k};
$valw->configure('-text' => ${$self->{'settings'}}->{'design'}->{$k});
} }
############## ##############
...@@ -168,16 +174,14 @@ sub _populate_widget { ...@@ -168,16 +174,14 @@ sub _populate_widget {
'-column' => $i++, '-column' => $i++,
); );
} }
# $fr_design->Label(
# -text => "Control",
# )->grid(
# '-row' => 0,
# '-column' => $i++,
# '-columnspan' => 2,
# );
my $designmap = DESIGNMAP; my $designmap = DESIGNMAP;
my $entry;
foreach my $k (qw(FREQUENCY BAUDRATE TIMER_WIDTH)) { foreach my $k (qw(FREQUENCY BAUDRATE TIMER_WIDTH)) {
$entry = $fr_design->Entry(
'-text' => ${$self->{'settings'}}->{'design'}->{$k},
'-width' => -1,
);
Tk::grid( Tk::grid(
$fr_design->Label( $fr_design->Label(
'-text' => DESIGNMAP->{$k}->{'ini_name'}, '-text' => DESIGNMAP->{$k}->{'ini_name'},
...@@ -185,10 +189,6 @@ sub _populate_widget { ...@@ -185,10 +189,6 @@ sub _populate_widget {
$fr_design->Label( $fr_design->Label(
'-text' => DESIGNMAP->{$k}->{'unit'}, '-text' => DESIGNMAP->{$k}->{'unit'},
), ),
$fr_design->Entry(
'-textvariable' => \${$self->{'settings'}}->{'design'}->{$k},
'-width' => -1,
),
# $fr_design->Button( # $fr_design->Button(
# -text => 'Defaults', # -text => 'Defaults',
# -command => [\&_save, $self], # -command => [\&_save, $self],
...@@ -198,9 +198,28 @@ sub _populate_widget { ...@@ -198,9 +198,28 @@ sub _populate_widget {
# $btn_save->configure(-state => $state); # $btn_save->configure(-state => $state);
# }, # },
# ), # ),
$entry,
'-sticky' => 'ew' '-sticky' => 'ew'
); );
$entry->configure(
'-validate' => 'key',
'-validatecommand' => [\&_validate_design_entry, $entry, $k],
);
$entry->bind('<Control-a>',
sub {
my $w = shift;
$w->selectionRange(0,'end');
$w->icursor('end');
},
);
# $entry->bind('<KeyRelease>',
# sub {
# my $entry_var = ${$entry->cget('-textvariable')};
# $self->_highlight_widget($entry, (FIJI::Settings::validate_design_value($k, \$entry_var)));
# }
# );
} }
$widget_background = $entry->cget('-bg');
############## ##############
# FIUs panel # # FIUs panel #
...@@ -208,6 +227,7 @@ sub _populate_widget { ...@@ -208,6 +227,7 @@ sub _populate_widget {
$self->_add_fiu_panel($fr); $self->_add_fiu_panel($fr);
} }
sub _add_fiu_panel { sub _add_fiu_panel {
my($self, $fr) = @_; my($self, $fr) = @_;
...@@ -277,23 +297,65 @@ sub _add_fiu ($$) { ...@@ -277,23 +297,65 @@ sub _add_fiu ($$) {
# forward declarations of widgets used in callbacks # forward declarations of widgets used in callbacks
my $lfsr_button = $fr_fius->Checkbutton(); my $lfsr_button = $fr_fius->Checkbutton();
my $mask_entry = $fr_fius->Entry( my $mask_entry = $fr_fius->Entry(
'-textvariable' => \$fiu->{'FIU_LFSR_MASK'},
'-width' => -1, '-width' => -1,
'-justify' => 'right', '-justify' => 'right',
); );
$mask_entry->{'fiji_backend_ref'} = \$fiu->{'FIU_LFSR_MASK'};
$mask_entry->configure('-text' => sprintf("0x%x", $fiu->{'FIU_LFSR_MASK'}));
$mask_entry->configure(
'-validate' => 'key',
'-validatecommand' => [\&_validate_fiu_entry, $mask_entry, 'FIU_LFSR_MASK'],
);
$mask_entry->bind('<Control-a>',
sub {
my $w = shift;
$w->selectionRange(0,'end');
$w->icursor('end');
},
);
my $so_entry = $fr_fius->Entry( my $so_entry = $fr_fius->Entry(
'-textvariable' => \$fiu->{'FIU_LFSR_STUCK_OPEN_BIT'},
'-width' => -1, '-width' => -1,
'-justify' => 'center', '-justify' => 'center',
); );
$so_entry->{'fiji_backend_ref'} = \$fiu->{'FIU_LFSR_STUCK_OPEN_BIT'};
$so_entry->configure('-text' => $fiu->{'FIU_LFSR_STUCK_OPEN_BIT'});
$so_entry->configure(
'-validate' => 'key',
'-validatecommand' => [\&_validate_fiu_entry, $so_entry, 'FIU_LFSR_STUCK_OPEN_BIT'],
);
$so_entry->bind('<Control-a>',
sub {
my $w = shift;
$w->selectionRange(0,'end');
$w->icursor('end');
},
);
# $so_entry->bind('<KeyRelease>',
# sub {
# my $entry_var = ${$so_entry->cget('-textvariable')};
# $self->_highlight_widget($so_entry, (FIJI::Settings::validate_fiu_value('FIU_LFSR_STUCK_OPEN_BIT', \$entry_var)));
# }
# );
my $lbl = $fr_fius->Label( my $lbl = $fr_fius->Label(
'-text' => "FIU$i", '-text' => "FIU$i",
); );
my $net_entry = $fr_fius->Entry( my $net_entry = $fr_fius->Entry(
'-textvariable' => \$fiu->{'FIU_NET_NAME'}, '-textvariable' => \$fiu->{'FIU_NET_NAME'},
'-width' => 25, '-width' => 25,
); );
$net_entry->bind('<Control-a>',
sub {
my $w = shift;
$w->selectionRange(0,'end');
$w->icursor('end');
},
);
my $model_menu = $fr_fius->Optionmenu( my $model_menu = $fr_fius->Optionmenu(
'-options' => FIU_MODEL->{'values'}, '-options' => FIU_MODEL->{'values'},
'-width' => -1, '-width' => -1,
...@@ -315,7 +377,7 @@ sub _add_fiu ($$) { ...@@ -315,7 +377,7 @@ sub _add_fiu ($$) {
}, },
); );
$model_menu->configure( $model_menu->configure(
-command => sub { '-command' => sub {
my $logger = get_logger(); my $logger = get_logger();
my $model = shift; my $model = shift;
$logger->trace("model is now: $model"); $logger->trace("model is now: $model");
...@@ -335,6 +397,7 @@ sub _add_fiu ($$) { ...@@ -335,6 +397,7 @@ sub _add_fiu ($$) {
'-variable' => \$fiu->{'FIU_LFSR_EN'}, '-variable' => \$fiu->{'FIU_LFSR_EN'},
'-command' => [\&_set_fields_by_button, $self, $lfsr_button, $mask_entry ], '-command' => [\&_set_fields_by_button, $self, $lfsr_button, $mask_entry ],
); );
Tk::grid( Tk::grid(
$lbl, $lbl,
$net_entry, $net_entry,
...@@ -358,6 +421,7 @@ sub _add_fiu ($$) { ...@@ -358,6 +421,7 @@ sub _add_fiu ($$) {
); );
} }
sub _update_fields { sub _update_fields {
my($self, my($self,
$lbl, $lbl,
...@@ -371,9 +435,6 @@ sub _update_fields { ...@@ -371,9 +435,6 @@ sub _update_fields {
) = @_; ) = @_;
my $logger = get_logger(); my $logger = get_logger();
# update LFSR mask first
$self->_set_fields_by_button($lfsr_button, $mask_entry);
my $model = ${$model_menu->cget('-variable')}; my $model = ${$model_menu->cget('-variable')};
if ($model eq 'RUNTIME' || if ($model eq 'RUNTIME' ||
$model eq 'STUCK_OPEN') { $model eq 'STUCK_OPEN') {
...@@ -412,4 +473,44 @@ sub _set_fields { ...@@ -412,4 +473,44 @@ sub _set_fields {
} }
} }
sub _validate_design_entry {
return _validate_entry(DESIGNMAP, @_);
}
sub _validate_fiu_entry {
return _validate_entry(FIUMAP, @_);
}
sub _validate_entry {
my ($map, $widget, $name, $new, $diff, $old, $char_idx, $type) = @_;
my $new_bak = $new;
my $ok = FIJI::Settings::validate_value($map, $name, \$new);
_highlight_widget($widget, (!$ok));
if ($ok) {
${$widget->{'fiji_backend_ref'}} = $new;
$widget->configure('-validate' => 'key');
}
return 1; # always allow the new value and show the user what happened.
}
sub _highlight_widget ($$) {
my($widget, $enable) = @_;
if ($enable) {
$widget->configure('-bg' => 'orange red');
} else {
# the 3rd element returned for '-bg' is the default background, usually.
# Apparently it is something darker on Linux so the following does
# not work as intended. :(
# $widget->configure('-bg' => ($widget->configure('-bg'))[3]);
# Work around: store the entry background color at creation time and
# use that instead.
$widget->configure('-bg' => $widget_background);
}
}
1; 1;
...@@ -3,6 +3,7 @@ FIU_NUM=8 ...@@ -3,6 +3,7 @@ FIU_NUM=8
TIMER_WIDTH=32 TIMER_WIDTH=32
ID=0x0123 ID=0x0123
BAUDRATE=115200 BAUDRATE=115200
FREQUENCY=50e6
[FIU0] [FIU0]
NET_NAME=siegfried NET_NAME=siegfried
...@@ -14,27 +15,48 @@ LFSR_MASK=0x0000 ...@@ -14,27 +15,48 @@ LFSR_MASK=0x0000
[FIU1] [FIU1]
NET_NAME=bla1 NET_NAME=bla1
FAULT_MODEL=PASS_THRU FAULT_MODEL=PASS_THRU
ENABLED_BY_LFSR=0
LFSR_BIT_FOR_STUCK_OPEN=0
LFSR_MASK=0x0
[FIU2] [FIU2]
NET_NAME=bla2 NET_NAME=bla2
FAULT_MODEL=STUCK_OPEN FAULT_MODEL=STUCK_OPEN
ENABLED_BY_LFSR=0
LFSR_BIT_FOR_STUCK_OPEN=0
LFSR_MASK=0x0
[FIU3] [FIU3]
NET_NAME=bla3 NET_NAME=bla3
FAULT_MODEL=SEU FAULT_MODEL=SEU
ENABLED_BY_LFSR=0
LFSR_BIT_FOR_STUCK_OPEN=0
LFSR_MASK=0x0
[FIU4] [FIU4]