--- /dev/null
+package CtsConfig;
+
+#default cts endpoint. can be overriden by a command line parameter
+sub getDefaultEndpoint {
+ return 0xf3c0;
+}
+
+1;
\ No newline at end of file
-# Module: Channel Masking (Type 0x00)
+# Module: Channel Configuration (Type 0x00)
# This block contains only one control register that holds
-# the bitmask (bits 15:0) for the internal channel selection.
+# - the bitmask (bits 15:0) for the internal channel selection (0 = off, 1 = on)
+# - the bitmask (bits 31:16) enable rising edge operation (0 = level, 1 = edge).
# A trigger event is detected, if any of the masked channels is asserted.
package CtsMod00;
use strict;
use TrbRegister;
-sub moduleName {"Channel Masking"}
+sub moduleName {"Channel Configuration"}
sub init {
my $self = $_[0];
# registers
$regs->{"trg_channel_mask"} = TrbRegister->new($address + 1, $trb, {
- 'mask' => {'lower' => 0, 'len' => 16, 'type' => 'mask'}
+ 'mask' => {'lower' => 0, 'len' => 16, 'type' => 'mask'},
+ 'edge' => {'lower' => 16, 'len' => 16, 'type' => 'mask'}
}, {
'accessmode' =>"rw",
'monitor' => '1',
# Module: Random Pulser (Type 0x50)
-# The random pulser generates irregular event patterns. As
-# the pulser is not configurable, the sole purpose of this block is to inform about the
-# presens of the unit and its mapping to the internal channel. The has no payload,
-# i.e. a fixed size of 0 words.
+# A random pulser generates irregular event patterns. Each instance is configured
+# with a single word control registers, which holds its threshold. There is a
+# linear depency between the average trigger rate F and the threshold T given by
+# F(T) = Freq_Base * T / Max_T = 100 MHz * T / Max_T
package CtsMod50;
my $header = $self->{'_cts'}{'_enum'}{0x50}->read();
+ for(my $i = 0; $i < $header->{'len'}; $i++) {
+ my $key = "trg_random_pulser_config$i";
+
+ $regs->{$key} = new TrbRegister($address + 1 + $i, $trb, {
+ 'threshold' => {'lower' => 0, 'len' => 32},
+ }, {
+ 'accessmode' => "rw",
+ 'label' => "Random Pulser Threshold $i",
+ 'monitor' => '1',
+ 'export' => 1
+ });
+ }
+
for(my $i = 0; $i < $header->{'itc_len'}; $i++) {
- $self->{'_cts'}->getProperties->{'itc_assignments'}[$i + $header->{'itc_base'}] = "Random Pulser";
+ $self->{'_cts'}->getProperties->{'itc_assignments'}[$i + $header->{'itc_base'}] = "Random Pulser $i";
}
# registers
- $prop->{"trg_random_pulser_count"} = 1;
+ $prop->{"trg_random_pulser_count"} = $header->{'len'};
}
1;
\ No newline at end of file
'monitor' => 1
});
+ $regs->{'cts_throttle'} = TrbRegister->new(0x0c + $debug_block, $trb, {
+ 'threshold' => {'lower' => 0, 'len' => 10},
+ 'enable' => {'lower' => 10, 'len' => 1, 'type' => 'bool'},
+ 'stop' => {'lower' => 31, 'len' => 1, 'type' => 'bool'}
+ }, {
+ 'accessmode' => "rw",
+ 'label' => "Trigger Throttle",
+ 'monitor' => 1,
+ 'export' => 1
+ });
+
$prop->{'cts_clock_frq'} = 1e8;
$prop->{'trb_endpoint'} = $trb->getEndpoint;
$prop->{'trb_compiletime'} = $trb->read(0x40);
# Trb/Cts IO
# use TrbSim; included in connectToCTS if required
- use TrbNet; #included in connectToCTS if required
+# use TrbNet; #included in connectToCTS if required
use Cts;
+ use CtsConfig;
# Misc
use POSIX qw[ceil];
$rate , $value];
}
}
-
+
+ printTable $tab;
+
if ($filename) {
# store json
my $json = JSON::PP->new->encode({
'monitor' => $monData
});
- open FH, ">$filename.js";
+ open FH, ">$filename/dump.js";
print FH $json;
close FH;
if ($#{ $plotData } > 4) {
- open FH, ">$filename.gpdata";
+ open FH, ">$filename/plot.data";
foreach (@{$plotData}) {
my @row = (@{ $_ });
$row[0] -= $plotData->[-1][0];
close FH;
my $fh = new FileHandle ("|gnuplot");
- $fh->autoflush(1);
-
- print $fh <<"EOF";
-set terminal png font "monospace,8" size 450,170
+ if ($fh) {
+ $fh->autoflush(1);
+
+ print $fh <<"EOF";
+set terminal svg font "monospace,8" size 450,185
set font
-set output "$filename.png"
+set output "$filename/plot.svg"
set grid
set key
set autoscale xfixmin
#set yrange [* : *<1000000]
-set xlabel "Time [s]"
-set ylabel "Rate [1/s]"
+set xlabel "Time since last update [s]"
+set ylabel "Rate [Hz]"
plot \\
- "$filename.gpdata" using 1:3:(\$3 / 1000) with yerrorlines title "Edges", \\
- "$filename.gpdata" using 1:4:(\$4 / 1000) with yerrorlines title "Accepted"
+ "$filename/plot.data" using 1:3:(\$3 / 1000) with yerrorlines title "Edges", \\
+ "$filename/plot.data" using 1:4:(\$4 / 1000) with yerrorlines title "Accepted"
EOF
- ;
- close $fh;
+ ;
+ close $fh;
+
+ print "Plot produced\n";
+ } else {
+ print "error while executing gnuplot\n";
+ }
}
}
- printTable $tab;
-
$lastRead = $read;
usleep($interval*1e3);
-
}
}
sub connectToCTS {
- my $mode = shift;
my $endpoint = shift;
my $trb;
- if ($mode eq 'sim') {
- eval {require "TrbSim.pm"};
- $trb = TrbSim->new($endpoint);
- my $fp;
- open $fp, "<memory.dump";
- $trb->loadDump($fp);
- close $fp;
-
- } else{
- eval {require "TrbNet.pm"};
- $trb = TrbNet->new($endpoint);
- }
+ eval {require "TrbNet.pm"};
+ $trb = TrbNet->new($endpoint);
return Cts->new($trb);
}
####################################################################################
-my $trbMode = 'net';
-my $endpoint = 0xf3c0;
+my $endpoint = CtsConfig->getDefaultEndpoint;
my $updateInterval = 1000;
my $rateNumber = 30;
if ($arg eq "-h" or $arg eq "--help") {
help();
exit();
- } elsif ($arg eq "-s" or $arg eq "--sim") {
- $trbMode = 'sim';
} elsif ($arg eq "-e" or $arg eq "--endpoint") {
unless ($i < @ARGV) {
$rateNumber = $ARGV[++$i];
} elsif ($arg eq "l" or $arg eq "list") {
- printTable commandList connectToCTS($trbMode, $endpoint);
+ printTable commandList connectToCTS($endpoint);
exit();
} elsif ($arg eq "d" or $arg eq "dump") {
- print commandDump connectToCTS($trbMode, $endpoint);
+ print commandDump connectToCTS($endpoint);
exit();
} elsif ($arg eq "r" or $arg eq "read") {
}
my @list = @ARGV[$i+1 .. $#ARGV];
- printTable commandRead(connectToCTS($trbMode, $endpoint), \@list);
+ printTable commandRead(connectToCTS($endpoint), \@list);
exit();
} elsif ($arg eq "w" or $arg eq "write") {
exit();
}
- my $cts = connectToCTS($trbMode, $endpoint);
+ my $cts = connectToCTS($endpoint);
commandWrite($cts, lc join(" ", @ARGV[$i+1 .. $#ARGV]));
exit();
} elsif ($arg eq "m" or $arg eq "monitor") {
- my $cts = connectToCTS($trbMode, $endpoint);
+ my $cts = connectToCTS($endpoint);
commandMonitor($cts, $ARGV[++$i], $updateInterval, $rateNumber);
} else {
--- /dev/null
+#!/bin/bash
+if [ $2 > 0 ]; then
+ host=`hostname`
+ port="1234"
+
+ echo "Trying to kill processes 'cts' and 'dhttpi'"
+ pkill "^(cts|dhttpi)$"
+
+ echo "Try to map monitoring files to shared memory (if it failes, no harm is"
+ echo " done. Only the HDD has to work a little bit more)"
+
+ rm -rf /dev/shm/cts-monitor
+ mkdir -p /dev/shm/cts-monitor
+# chmod 666 /dev/shm/cts-monitor
+ ln -s /dev/shm/cts-monitor htdocs/monitor
+ mkdir -p htdocs/monitor
+
+ echo "Start webserver at http://$host:$port"
+ ./httpi $host $port &
+
+ echo "Start monitoring script"
+ until ./cts m htdocs/monitor ; do
+ echo " - Monitor crashed with exit code $?. Respawning.." >&2
+ sleep 1
+ done
+else
+ xterm -fn "-misc-fixed-medium-r-normal--8-*-*-*-*-*-iso8859-15" \
+ +sb -geometry 200x100 +aw +bc -bg LightCoral -j -e ./cts_gui 1 1
+fi;
\ No newline at end of file
use lib "./include/";
use Cts;
+use CtsConfig;
use JSON::PP;
sub connectToCTS {
- my $mode = shift;
my $endpoint = shift;
my $trb;
- if ($mode eq 'sim') {
- eval {require "TrbSim.pm"};
- $trb = TrbSim->new($endpoint);
- my $fp;
- open $fp, "<memory.dump";
- $trb->loadDump($fp);
- close $fp;
- } else{
- eval {require "TrbNet.pm"};
- $trb = TrbNet->new($endpoint);
- }
+ eval {require "TrbNet.pm"};
+ $trb = TrbNet->new($endpoint);
return Cts->new($trb);
}
-my $cts = connectToCTS 'trb', 0xf3c0;
+
+my $cts = connectToCTS( CtsConfig->getDefaultEndpoint );
my $query = $ENV{'QUERY_STRING'};
}
print JSON::PP->new->allow_blessed->convert_blessed->encode(\%result);
-} elsif ($query =~ /^write,([\w\d_,\.]+)$/) {
+} elsif ($query =~ /^write,([\w\d_,\.\[\]]+)$/) {
my @values = split /,/, $1;
my $regs = {};
+ $cts->getTrb->startCachedWrites();
+
while (my $key = shift @values) {
- if ($key =~ /^(.*)\.(.*)$/) {
+ if ($key =~ /^(.*)\.(.*)\[(\d+)\]$/) {
+ my $val = $cts->getRegisters->{$1}->read()->{$2};
+ $regs->{$1}{$2} = ($val & (0xFFFFFFFF ^ (1 << $3))) | (((shift @values) & 1) << $3);
+ } elsif ($key =~ /^(.*)\.(.*)$/) {
$regs->{$1} = {} unless ref $regs->{$1};
$regs->{$1}{$2} = shift @values;
} else {
foreach my $key (keys $regs) {
my $reg = $cts->getRegisters->{$key};
- next unless defined $reg;
+ #next unless defined $reg;
$reg->write($regs->{$key});
}
+
+ $cts->getTrb->stopCachedWrites();
print "1";
}
<div id="header">
<div class="content">
<div id="data-update">Update</div>
+ <div id="status-indicator" class="warning"></div>
<h1>Central Trigger System</h1>
</div>
</div>
<div class="header"><span class="indicator"></span> Status overview</div>
<div class="content">
- <img src="monitor.png" id="rate-plot" />
+ <img src="monitor/plot.svg" id="rate-plot" />
<table id="overview-tab">
<tr>
<th class="label">Counter</th>
<th class="rate">Rate</th>
</tr>
- <tr>
+ <tr class="alt">
<td class="label">Trigger asserted</td>
<td class="value autorate autoratevalue" slice="cts_cnt_trg_asserted.value" suffix=" clks.">n/a</td>
<td class="rate autorate" slice="cts_cnt_trg_asserted.value" suffix=" s<sup>-1</sup>">n/a</td>
<td class="rate autorate" slice="cts_cnt_trg_edges.value" suffix=" Hz">n/a</td>
<tr>
- <tr>
+ <tr class="alt">
<td class="label">Trigger accepted</td>
<td class="value autorate autoratevalue" slice="cts_cnt_trg_accepted.value" suffix=" events">n/a</td>
<td class="rate autorate" slice="cts_cnt_trg_accepted.value" suffix=" Hz">n/a</td>
<td colspan="3"> </td>
</tr>
- <tr>
+ <tr class="alt">
<td class="label">Last Idle Time</td>
<td class="value autoupdate" slice="cts_cnt_idle_time.value" format="countToTime" suffix=" ns">n/a</td>
<td class="rate "></td>
<td class="value autoupdate" slice="cts_cnt_dead_time.value" format="countToTime" suffix=" ns">n/a</td>
<td class="rate autoupdate" slice="cts_cnt_dead_time.value" format="countToFreq">n/a</td>
<tr>
- </table>
- </div>
- </div>
-
- <div class="expandable expanded" id="itc-expander">
- <div class="header"><span class="indicator"></span> Trigger Channels</div>
-
- <div class="content">
- <table id="itc-tab0" class="itc">
+
<tr>
- <th class="channel">#</th>
- <th class="enable">Ena</th>
- <th class="assign">Assignment</th>
- <th class="type">Trigger Type</th>
- <th class="rate">Current Rate</th>
+ <td colspan="3"> </td>
</tr>
- </table>
-
- <table id="itc-tab1" class="itc">
+
+ <tr class="alt">
+ <td class="label">Throttle</td>
+ <td colspan="2" class="rate">
+ <input type="checkbox" class="autoupdate autocommit" slice="cts_throttle.enable" /> Limit Trigger Rate to
+ <input class="autocommit autoupdate" slice="cts_throttle.threshold" style="width: 4em;"
+ format="var f=function(x){return parseNum(x)+1}; f" interpret="var f=function(x){return parseNum(x)-1;}; f" /> KHz
+ </td>
+ </tr>
+
<tr>
- <th class="channel">#</th>
- <th class="enable">Ena</th>
- <th class="assign">Assignment</th>
- <th class="type">Trigger Type</th>
- <th class="rate">Current Rate</th>
+ <td class="label">Full Stop</td>
+ <td colspan="2" class="rate">
+ <input type="checkbox" class="autoupdate autocommit" slice="cts_throttle.stop" id="fullstop"
+ format="var f=function(x, y, e){e.getParent().getParent()[x == true ? 'addClass' : 'removeClass']('fullstop'); return x}; f"
+ interpret="id" /> Ignore all events
+ </td>
</tr>
</table>
</div>
</div>
-
- <div class="expandable expanded" id="inputs-expander">
- <div class="header"><span class="indicator"></span> Trigger Input Configuration</div>
+
+ <div class="expandable expanded" id="itc-expander">
+ <div class="header"><span class="indicator"></span> Trigger Channels</div>
<div class="content">
- <table id="inputs-tab">
- <tr>
- <th class="num">#</th>
- <th class="invert">Invert</th>
- <th class="delay">Delay</th>
- <th class="spike">Spike Rejection</th>
- <th class="override">Override</th>
- <th class="rate">Current Rate</th>
- </tr>
- </table>
+ <table id="itc-tab" class="itc">
+ <tr>
+ <th class="channel">#</th>
+ <th class="enable">Enable</th>
+ <th class="edge"><abbr title="Trigger Condition: Either sensitive to the rising edge or a high level">Trg. Cond.</abbr></th>
+ <th class="assign">Assignment</th>
+ <th class="type">TrbNet Type</th>
+ <th class="rate">Asserted</th>
+ <th class="rate">Edges</th>
+ </tr>
+ </table>
</div>
</div>
- <div class="expandable expanded" id="coin-expander">
- <div class="header"><span class="indicator"></span> Coincidence Detectors and Regular Pulsers</div>
+ <div class="expandable expanded" id="inputs-expander">
+ <div class="header"><span class="indicator"></span> Trigger Input Configuration and Coincidence Detectors</div>
<div class="content">
<div class="left">
+ <h3>Input Modules</h3>
+ <table id="inputs-tab">
+ <tr>
+ <th class="num">#</th>
+ <th class="rate">Inp. Rate</th>
+ <th class="invert">Invert</th>
+ <th class="delay">Delay</th>
+ <th class="spike"><abbr title="Noise reducing. High pulses shorter than this values are rejeted">Spike Rej.</abbr></th>
+ <th class="override">Override</th>
+ </tr>
+ </table>
+ </div>
+
+ <div class="right">
<h3>Coincidence Detectors</h3>
<table id="coin-tab">
<tr>
<th class="num">#</th>
<th class="window">Window</th>
- <th class="coin">Coin Mask</th>
- <th class="inhibit">Inhibit Mask</th>
+ <th class="coin"><abbr title="Inputs that are required to rise within the specified window of time (edge sensitive)">Coin Mask</acronym></th>
+ <th class="inhibit"><abbr title="Additionally to the Coin Mask, this input have to be asserted (level sensitive)">Inhibit Mask</abbr></th>
</tr>
</table>
</div>
-
- <div class="right">
- <h3>Periodical Pulser</h3>
+ </div>
+ </div>
+
+ <div class="expandable expanded" id="coin-expander">
+ <div class="header"><span class="indicator"></span> Pulsers</div>
+
+ <div class="content">
+ <div class="left pulser-content">
+ <h3>Periodical Pulsers</h3>
<table id="pulser-tab">
<tr>
<th class="num">#</th>
</tr>
</table>
</div>
+
+ <div class="right rand-pulser-content">
+ <h3>Random Pulsers</h3>
+ <table id="rand-pulser-tab">
+ <tr>
+ <th class="num">#</th>
+ <th class="freq">Mean Frequency</th>
+ </tr>
+ </table>
+ </div>
</div>
</div>
<td class="value"><input type="checkbox" class="autoupdate autocommit" slice="cts_readout_config.trg_cnt" /> Trigger statistics</td>
</tr>
- <tr>
- <td> </td>
- <td class="value"><input type="checkbox" class="autoupdate autocommit" slice="cts_readout_config.wasa" /> WASA data</td>
- </tr>
-
<tr class="alt">
- <td class="label">TD FSM Limit:</td>
+ <td class="label">TD FSM Limit (debug only):</td>
<td class="value">
<input class="autocommit autoupdate text" slice="cts_fsm_limits.td"
format="var f=function(x){return parseInt(x)==0xffff ? 'disabled' : (parseInt(x) ? x + ' events' : 'active')}; f"
</tr>
<tr>
- <td class="label">RO FSM Limit:</td>
+ <td class="label">RO FSM Limit (debug only):</td>
<td class="value">
<input class="autocommit autoupdate text" slice="cts_fsm_limits.ro"
format="var f=function(x){return parseInt(x)==0xffff ? 'disabled' : (parseInt(x) ? x + ' events' : 'active')}; f"
padding:0;
margin: 0;
text-align: center;
- background: 0 40px url(gradient.png) repeat-x #F4F5F5;
+ background: #F4F5F5;
font-family: arial, sans-serif;
font-size: 10pt;
#header .content {
width: 1000px;
margin: 0 auto 0 auto;
- padding-top: 12px;
+ padding-top: 8px;
text-align: left;
}
border-bottom: 1px solid #DDDDDD;
background: #F9F9F9;
height: 40px;
+ position: fixed;
+ left: 0; top: 0;
+ width: 100%;
}
#header h1 {
margin: 0;
font-size: 1.5em;
+ float: left;
}
h3 {
#content-area {
width: 1000px;
- margin: 20px auto 20px auto;
+ margin: 60px auto 20px auto;
text-align: left;
}
border-bottom: 1px solid #dddddd;
}
+ #status-indicator {
+ height: 30px;
+ width: 30px;
+ margin: -3px 10px 0 0;
+
+ float: left;
+
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+
+ background-color: #30ff30;
+ }
+
+ #status-indicator.warning {background-color: #ffe823;}
+ #status-indicator.error {background-color: #ff8080;}
+
+ div.content > div.left {
+ padding-right: 9px;
+ border-right: 2px solid #6060ff;
+ }
+
+
/* Table Generics */
table {
border-collapse: collapse;
- width: 450px;
+ width: 100%;
}
table td, table tr {
background: #ffffd0;
}
- table td {
- vertical-align: top;
- width: 20%;
- }
-
.alt td {
background: #f6f6f6;
border-bottom: 1px solid #e0e0e0;
input.uncommitted {font-style: italic; color: #0000ff;}
/* Overview */
+ #overview-tab {width: 470px}
#overview-tab .rate,
#overview-tab .value {text-align: right;}
+ #overview-tab .fullstop {background: #ffd0d0; color: #ff0000; font-weight: bold;}
+
#rate-plot {
float: right;
width: 450px;
- height: 170px;
+ height: 185px;
margin-right: 20px;
}
/* Trigger Inputs */
- #inputs-tab {width: 100%;}
#inputs-tab input {width: 4em;}
#inputs-tab .num {width: 2%; text-align: center;}
#inputs-tab .invert {text-align: center;}
+
+ #inputs-tab .rate {text-align: right;}
/* Internal Trigger Channel */
- #itc-tab0, .left {
+ .content div.left {
float: left;
width: 470px;
}
- #itc-tab1, .right {
+ .content div.right {
margin-left: 490px;
}
width: 8em;
}
- .itc .enable {
- width: 8%;
- text-align: center;
-
- }
-
- .itc .channel {width: 5%; text-align: center;}
- .itc .assign {width: 45%}
- .itc .type {width: 20%;}
- .itc .type select {width: 6em;}
- .itc .rate {width: 20%; text-align: right;}
+ .itc .channel {width: 2%; text-align: center;}
+ .itc .enable {width: 6%; text-align: center;}
+ .itc .edge {width: 10%; text-align: center;}
+/* .itc .assign {width: 20%} */
+ .itc .rate {width: 10%; text-align: right;}
+
+ .itc td.rate {color: #909090;}
+ .itc td.active {color: #000; font-weight: bold;}
/* Coincidence Detection */
- #coin-tab {
- width: 100%;
- }
-
- #coin-tab .num {
- width: 4%;
- }
-
+ #coin-tab .num {width: 4%;}
#coin-tab .window {width: 32%;}
#coin-tab .window input {width: 4em; text-align: right}
#coin-tab .coin {width: 32%;}
#coin-tab .inhibt {width: 32%;}
/* Pulser */
- #pulser-tab {width: 100%;}
#pulser-tab .num {width: 4%; text-align: center}
#pulser-tab .period {text-align: right}
#pulser-tab .freq {width: 30%; text-align: right}
+/**
+ * Similiarly to parseInt/parseFloat, but can also handle binary
+ * numbers indicated by the "0b" prefix. If no prefix in combination
+ * with a decimal point "." is used, the number is interpret as float.
+ */
function parseNum(str) {
+ if(typeof(str) == 'number') return str;
+
str = str.trim().toLowerCase();
var result = 0;
if (str.substr(0,2) == "0b") {
return str.match(/\./) ? parseFloat(str) : parseInt(str, str.match(/^\s*0x/) ? 16 : 10);
}
+/**
+ * Takes a slice expresion in the following formats:
+ * - register
+ * - register.slice
+ * - register.slice[bit]
+ * and returns an hash with the keys
+ * 'reg', 'slice', 'bit', 'exp'.
+ * If a property is not given by the expression, the
+ * corresponding value in the hash is set to undefined */
+function parseSlice(slice) {
+ var m = slice.match(/^(.+)\.([^\[]+)(\[\d+\]|)$/);
+ if (!m) return {
+ 'reg': slice,
+ 'slice': undefined,
+ 'bit': undefined,
+ 'exp': slice
+ };
+ return {
+ 'reg': m[1],
+ 'slice': m[2],
+ 'bit': m[3] ? parseInt(m[3].substr(1, m[3].length - 2)) : undefined,
+ 'exp': slice
+ };
+}
+
+function parseCallback(elem, func, fallback) {
+ if (elem[func])
+ return elem[func];
+
+ if (elem.get(func))
+ return elem[func] = eval(elem.get(func));
+
+ return fallback ? fallback : id;
+}
+
var CTS = new Class({
Implements: [Events],
defs: null,
this.renderTriggerInputs();
this.renderCoins();
this.renderRegularPulsers();
+ this.renderRandPulsers();
this.renderCTSDetails();
this.initAutoRates();
this.dataUpdate();
this.addEvent('dataUpdate', function() {
- $('rate-plot').set('src', $('rate-plot').get('src').split('?')[0] + "?" + new Date());
+ $('rate-plot').set('src', $('rate-plot').get('src').split('?')[0] + "?" + new Date().getTime());
});
+ this.addEvent('dataUpdate', this.updateStatusIndicator.bind(this));
this.initAutoCommit();
+
+ this.initSliceTitle();
},
readRegisters: function(regs, callback, formated) {
var dup = $('data-update');
dup.removeClass('error').set('text', 'Update').setStyle('display', 'block');
+ // hard-reset. if request's timeout fails, this is the last resort ...
+ var manualTimeout = window.location.reload.delay(10000);
+
new Request.JSON({
- url: 'monitor.js',
+ url: 'monitor/dump.js',
timeout: 200,
onSuccess: function(data) {
+ window.clearTimeout(manualTimeout);
if (parseInt(data.interval))
this.dataUpdate.delay(parseInt(data.interval) + 200, this);
else
if (this.currentData.time == data.time) {
this.dataUpdateConstantFor += 1;
- if (this.dataUpdateConstantFor > 2)
+ if (this.dataUpdateConstantFor > 2) {
dup.set('html', 'No change of timestamp since ' + this.dataUpdateConstantFor + ' fetches.<br />Server-Timestamp: ' + data.servertime).setStyle('display', 'block');
+ $('status-indicator').set('class', 'error');
+ return;
+ }
+
} else {
this.dataUpdateConstantFor = 0;
}
if (this.defs.properties.trb_endpoint != data.endpoint) {
dup.set('text', 'Data from incompatible endpoint: 0x' + parseInt(data.endpoint).toString(16)).addClass('error').setStyle('display', 'block');
+ $('status-indicator').set('class', 'error');
return;
}
}.bind(this),
onFailure: function() {
+ window.clearTimeout(manualTimeout);
this.dataUpdate.delay(1000, this);
dup.addClass('error').set('text', 'Update failed').setStyle('display', 'block');
+ $('status-indicator').set('class', 'error');
}.bind(this)
}).send();
},
if (e.hasClass('autoratevalue')) {
var count = data.rates[ e.get('slice') ].value;
- if (e.get('format')) {
- count = eval(e.get('format'))(count);
- }
+ count = parseCallback(e, 'format')(count);
var prefix = e.get('prefix');
if (prefix == null) prefix = "";
} else {
var rate = data.rates[ e.get('slice') ].rate;
- if (e.get('format')) {
- rate = eval(e.get('format'))(rate);
- } else if (!e.get('suffix')) {
- rate = formatFreq(rate);
- }
+ rate = parseCallback(e, 'format', formatFreq)(rate);
var prefix = e.get('prefix');
if (prefix == null) prefix = "";
return;
}
if (e.focussed) return;
- var s = e.get('slice');
- var reg = s.split('.')[0];
- var slice = s.split('.')[1];
- if (!slice) slice = "_compact";
+ var s = parseSlice(e.get('slice'));
+ if (!s.slice) s.slice = "_compact";
+ var value = data.monitor[s.reg][e.get('type') == 'checkbox' || s.bit != undefined || e.hasClass('autoupdate-value') ? 'v' : 'f'][s.slice];
+ if (s.bit != undefined) value = (parseInt(value) >> s.bit) & 1;
+
+ if (e.format || e.get('format')) {
+ value = parseCallback(e, 'format')(value, data.monitor[s.reg], e)
+ } else if (value.replace) {
+ value = value.replace(/, /g, ',<br />')
+ }
+
if (e.get('type') == 'checkbox') {
- var m = slice.match(/^(.+)\[(\d+)\]$/);
- if (m) {
- e.set('checked', data.monitor[reg].v[m[1]] & (1 << parseInt(m[2])) ? 'checked' : '');
- } else {
- e.set('checked', data.monitor[reg].v[slice] ? 'checked' : '');
- }
+ e.set('checked', value ? 'checked' : '');
} else {
- var value = data.monitor[reg][e.hasClass('autoupdate-value') ? 'v' : 'f'][slice];
-
-
- if (e.get('format')) {
- value = eval(e.get('format'))(value, data.monitor[reg], e)
- } else if (value.replace) {
- value = value.replace(/, /g, ',<br />')
- }
-
var prefix = e.get('prefix');
if (prefix == null) prefix = "";
var suffix = e.get('suffix');
/**
* This method handles all "autocommit"-elements. It is registered
- * as an event listener to the 'dataUpdate' event and hence is automatically
+ * as an event listener to all those elements and hence is automatically
* invoked as soon as new data is available
*
* You dont need to call this method manually.
var tmp = {};
if (!e.hasClass('autocommit')) alert('Value cannot be commited');
- if (e.get('type') == 'checkbox') {
- var m = e.get('slice').match(/^(.+)\.(.+)\[(\d+)\]$/);
- if (m) {
- var val = 0;
- var reg = m[1];
- var slice = m[2];
-
- $$('.autocommit').each(function(e) {
- var m = e.get('slice').match(/^(.+)\[(\d+)\]$/);
- if (!m || m[1] != reg + "." + slice) return;
- val |= e.get('checked') != "" ? 1 << parseInt(m[2]) : 0;
- });
-
- tmp[m[1]+'.'+m[2]] = val;
- } else {
- tmp[e.get('slice')] = (e.get('checked') != "" ? '1' : '0');
- }
- } else {
- var splitSlice = e.get('slice').split(/./);
- var reg = splitSlice[0], slice = splitSlice[1];
+ var s = parseSlice(e.get('slice'));
+
+ var value = e.get('value');
+ if (e.get('type') == 'checkbox')
+ value = (e.get('checked') != "" ? '1' : '0');
- var value = e.get('value');
- if (e.get('interpret')) {
- value = eval( e.get('interpret') )(value, e)
-
- if (e.get('format'))
- e.set('value', eval(e.get('format'))(value, e));
- }
+ if (e.interpret || e.get('interpret')) {
+ value = parseCallback(e, 'interpret')(value, e);
- tmp[e.get('slice')] = value;
- e.valueOnEnter = e.get('value');
+ if (e.format || e.get('format')) {
+ if (e.get('type') == 'checkbox')
+ e.set('checked', parseNum(parseCallback(e, 'format')(value, undefined, e)) ? 'checked' : '');
+ else
+ e.set('value', parseCallback(e, 'format')(value, undefined, e));
+ }
}
+ tmp[s.exp] = value;
+ e.valueOnEnter = e.get('value');
+
this.writeRegisters(tmp);
e.removeClass('uncommitted');
}
});
},
+
+/**
+ * Adds address information to the title of all elements with slice
+ * attribute
+ */
+ initSliceTitle: function() {
+ $$('*[slice]').each(function(e) {
+ var s = parseSlice(e.get('slice'));
+ var reg = this.defs.registers[s.reg];
+ var bits = "";
+
+ if (s.bit != undefined) {
+ bits = ' Bit ' + (parseInt(reg._defs[s.slice].lower) + s.bit);
+ } else if (s.slice) {
+ bits = ' Bits ' + (parseInt(reg._defs[s.slice].lower) + parseInt(reg._defs[s.slice].len) - 1) + ':' + reg._defs[s.slice].lower;
+ }
+
+ e.set('title',
+ s.exp + ': Address ' + formatAddress(reg._address) + bits
+ + (e.get('title') ? ' ' + e.get('title') : '')
+ );
+ }.bind(this));
+ },
/**
* Creates all active elements of the Trigger Input Configuration
var reg = 'trg_input_config' + i;
$('inputs-tab')
.adopt(
- new Element('tr')
+ new Element('tr', {'class': i%2?'':'alt'})
.adopt(
new Element('td', {'class': 'num', 'text': i})
+ ).adopt(
+ new Element('td', {'class': 'rate autorate', 'slice': 'trg_input_edge_cnt' + i + '.value', 'text': 'n/a', 'id': 'inp-rate' + i})
).adopt(
new Element('td', {'class': 'invert'})
.adopt(
new Element('option', {'value': 'to_high', 'text': '-> 1'})
)
)
- ).adopt(
- new Element('td', {'class': 'rate autorate', 'slice': 'trg_input_edge_cnt' + i + '.value', 'text': 'n/a', 'id': 'inp-rate' + i})
)
);
}
*/
renderTriggerChannels: function() {
for(var i=0; i < 16; i++) {
- var ddType;
- $('itc-tab' + (i / 8).toInt())
+ if ("unconnected" == this.defs.properties.itc_assignments[i]) continue;
+ var ddType, edgeType, assertedRate, edgeRate;
+ $('itc-tab') // + (i / 8).toInt())
.adopt(
- new Element('tr')
+ new Element('tr', {'class': i%2?'':'alt'})
.adopt(
new Element('td', {'text': i, 'class': 'channel'})
).adopt(
.adopt(
new Element('input', {'type': 'checkbox', 'id': 'itc-enable'+i, 'class': 'autocommit autoupdate', 'slice': 'trg_channel_mask.mask[' + i + ']'})
)
+ ).adopt(
+ new Element('td', {'class': 'edge'})
+ .adopt(
+ edgeType = new Element('select', {'type': 'checkbox', 'class': 'autocommit autoupdate', 'slice': 'trg_channel_mask.edge[' + i + ']'})
+ .adopt(
+ new Element('option', {'value': '0', 'text': 'H. Level'})
+ ).adopt(
+ new Element('option', {'value': '1', 'text': 'R. Edge'})
+ )
+ )
).adopt(
new Element('td', {'class': 'assign', 'text': this.defs.properties.itc_assignments[i]})
).adopt (
.adopt(
ddType = new Element('select', {'class': 'autocommit autoupdate autoupdate-value', 'slice': '_trg_trigger_types' + (i < 8 ? '0' : '1') + '.type' + i})
)
- ).adopt(
- new Element('td', {'class': 'rate autorate', 'slice': 'trg_channel_edge_cnt' + i + '.value', 'text': 'n/a', 'id': 'itc-rate' + i})
- ));
+ ).adopt (
+ assertedRate = new Element('td', {'class': 'rate autorate', 'slice': 'trg_channel_asserted_cnt' + i + '.value', 'text': 'n/a', 'id': 'itc-asserted-rate' + i})
+ ).adopt (
+ edgeRate = new Element('td', {'class': 'rate autorate', 'slice': 'trg_channel_edge_cnt' + i + '.value', 'text': 'n/a', 'id': 'itc-edge-rate' + i})
+ )
+ );
for(var j=0; j < 16; j++)
ddType.adopt(new Element('option', {'value': j, 'text': this.defs.registers['_trg_trigger_types' + (i < 8 ? '0' : '1')]._defs['type' + i].enum[j]}));
+
+ edgeType.format = edgeType.interpret = function(x, y, t) {
+ if (!t) t = y;
+ x = x ? 1 : 0;
+ var tds = t.getParent().getParent().getElements('td.rate');
+ tds[x].addClass('active');
+ tds[(x+1)%2].removeClass('active');
+
+ return x;
+ };
};
},
* should not by called manually.
*/
renderCoins: function() {
- $$("#coin-tab th.coin, #coin-tab th.inhibit").each(function(e){
+ $$("#coin-tab th.coin abbr, #coin-tab th.inhibit abbr").each(function(e){
e.set("text", e.get('text').match(/^(.*)(\(.*\))?/)[1]
+ " (" + (this.defs.properties.trg_input_count-1) + ":0)");
}.bind(this));
renderRegularPulsers: function() {
var cnt = this.defs.properties.trg_pulser_count;
if (!cnt) {
- $$('#pulser-expander .content').set('text', "This specific CTS design does not support regular pulsers");
+ $$('#pulser-expander .pulser-content').set('text', "This specific CTS design does not support regular pulsers");
return;
}
if (m[1] == 'k') val *= 1e3;
if (m[1] == 'm') val *= 1e6;
- val = Math.round ( 1e6 * (1/ val - 1/this.defs.properties.cts_clock_frq) );
+ val = Math.round ( 1e8 * (1/ val - 1/this.defs.properties.cts_clock_frq) );
if (store) elem.set('value', val);
}
}
elem[changed ? 'addClass' : 'removeClass']('unsaved');
- $('pulser-freq'+i).set('text', (changed ? "(CTS not updated) " : "") + formatFreq(1 / (val / 1e6 + 1 / this.defs.properties.cts_clock_frq)));
+ $('pulser-freq'+i).set('text', (changed ? "(CTS not updated) " : "") + formatFreq(1 / (val / 1e8 + 1 / this.defs.properties.cts_clock_frq)));
+ }
+ },
+
+/**
+ * Creates all active elements of the Pseudorandom Pulser
+ * section. It is called by the class' constructor and hence
+ * should not by called manually.
+ */
+ renderRandPulsers: function() {
+ var cnt = this.defs.properties.trg_random_pulser_count;
+ if (!cnt) {
+ $$('#pulser-expander .content .pulser-content').set('text', "This specific CTS design does not support pseudorandom pulsers");
+ return;
+ }
+
+ for(var i=0; i < cnt; i++) {
+ var inp;
+ $('rand-pulser-tab').adopt(
+ new Element('tr', {'class': i%2?'':'alt'})
+ .adopt(
+ new Element('td', {'class': 'num', 'text': i})
+ ).adopt(
+ new Element('td', {'class': 'freq'}).adopt(
+ inp = new Element('input', {
+ 'slice': 'trg_random_pulser_config'+i+'.threshold',
+ 'value': 'n/a',
+ 'class': 'autocommit autoupdate',
+ 'interpret': 'InterpretToRandPulserThreshold',
+ 'format': 'FormatRandPulserThreshold'
+ })
+ )
+ )
+ );
}
},
renderCTSDetails: function() {
$('trb_compiletime').set('text', new Date(this.defs.properties.trb_compiletime * 1000 - new Date().getTimezoneOffset() * 60000).toGMTString().replace('GMT', ''));
$('trb_endpoint').set('text', "0x" + this.defs.properties.trb_endpoint.toString(16));
+ },
+
+ updateStatusIndicator: function(data) {
+ var elem = $('status-indicator'), cls = 'error';
+ var fullstop = $('fullstop').get('checked');
+
+ if (data.rates['cts_cnt_trg_accepted.value'].rate > 0) {
+ cls = 'okay';
+ } else if (data.rates['cts_cnt_trg_asserted.value'].rate < 1 || fullstop) {
+ if (data.monitor.cts_td_fsm_state.f.state == 'TD_FSM_IDLE') cls = 'warning';
+ }
+
+ elem.set('class', cls);
+
+ if (cls == 'okay') {elem.set('title', 'CTS currently accepts triggers. Everything seems alright');}
+ else if (cls=='warning') {elem.set('title', 'No events were accepted, but CTS seems ready.' + (fullstop ? ' Full Stop active!' : 'Maybe no events occured?'));}
+ else {elem.set('title', 'CTS is neither idle, nor did it accept any events. Stuck at ' +
+ data.monitor.cts_td_fsm_state.f.state + ' and ' + data.monitor.cts_ro_fsm_state.f.state);}
}
});
return val.replace(',', '.');
}
-
+
+function InterpretToRandPulserThreshold(v) {
+ var freq = parseNum(v);
+
+ if (v.match(/khz/i)) freq *= 1e3;
+ else if (v.match(/mhz/i)) freq *= 1e6;
+
+ freq = Math.min(cts.defs.properties.cts_clock_frq, Math.max(freq, 0));
+
+ return Math.round(freq / cts.defs.properties.cts_clock_frq * 0x7FFFFFFF);
+};
+
+function FormatRandPulserThreshold(v) {
+ return formatFreq(Math.round(cts.defs.properties.cts_clock_frq * v / 0x7FFFFFFF), 0);
+};
+
+function formatAddress(x) {
+ var hex = parseNum(x).toString(16);
+ while(hex.length < 4) hex = "0" + hex;
+ return "0x" + hex;
+}
+
var GuiExpander = new Class({
boxes: null,
states: {},
});
var guiExpander;
window.addEvent('domready', function() {guiExpander = new GuiExpander();});
+
+function id(x) {return x;}
\ No newline at end of file
$logfile = "./htdocs/access.log";
$path = "./htdocs";
$sockaddr = 'S n a4 x8';
+
$server_host = $ARGV[0];
$server_port = $ARGV[1];
%content_types = (%system_content_types, %content_types);
undef %system_content_types;
-if ($pid = fork()) { exit; }
+#if ($pid = fork()) { exit; }
$0 = "dhttpi: binding port ...";
$bindthis = pack($sockaddr, 2, 1234, pack('C4', 0, 0, 0, 0));
socket(S, 2, 1, 6);
my $self = {
'_endpoint' => $endpoint,
'_known_registers' => {},
- '_prefetch' => {}
+ '_prefetch' => {},
+ '_write_cache' => {},
+ '_cached_writes' => 0
};
bless($self, $type);
my $withTime = shift;
if (1 == $len) {
+ # In write cache ?
+ my $write = $self->{'_write_cache'}{$address};
+ if (defined $write) {
+ return $withTime ? {'time' => -1, 'value' => $write} : $write;
+ }
+
+ # In prefetch cache ?
my $pre = $self->{'_prefetch'}{$address};
if (ref $pre and (not $withTime or exists $pre->{'time'})) {
return $withTime ? $pre : $pre->{'value'};
}
-
+
+ # Need to read directly from device
my $read = $withTime ?
trb_registertime_read($self->getEndpoint, $address) :
trb_register_read($self->getEndpoint, $address);
return $read->{$self->getEndpoint};
} else {
+ # TODO: Add write cache !!!
my $read = trb_register_read_mem($self->getEndpoint, $address, 0, $len);
defined $read or die(trb_strerror());
return $read->{$self->getEndpoint};
$self->{'_prefetch'}{ref $reg ? $reg->getAddress : $reg} = 1;
}
+sub startCachedWrites {
+ my $self = shift;
+ $self->{'_cached_writes'} = 1;
+}
+
+sub stopCachedWrites {
+ my $self = shift;
+ $self->flushWriteCache();
+ $self->{'_cached_writes'} = 1;
+}
+
+sub flushWriteCache {
+ my $self = shift;
+ my $cache = $self->{'_write_cache'};
+
+ if ($cache) {
+ foreach my $address (keys $cache) {
+ trb_register_write($self->getEndpoint, $address, $cache->{$address}) or die(trb_strerror);
+ }
+ }
+
+ $self->{'_write_cache'} = {};
+}
+
sub prefetch {
my $self = shift;
my $withTime = shift;
if (not ref $data) {
printf "// w 0x%04x 0x%04x 0x%08x\n", $self->getEndpoint, $address, $data;
- trb_register_write($self->getEndpoint, $address, $data) or die(trb_strerror);
+ if ($self->{'_cached_writes'}) {
+ $self->{'_write_cache'}{$address} = $data;
+ } else {
+ trb_register_write($self->getEndpoint, $address, $data) or die(trb_strerror);
+ }
} elsif (ref $data eq "ARRAY") {
for(my $i=0; $i < @$data; $i++) {
$self->write($address + $i, $data->[$i]);
+++ /dev/null
-package TrbSim;
-use warnings;
-use strict;
-
-sub new {
- my $type = $_[0];
- my $endpoint = $_[1];
- my $self = {
- '_data' => {},
- '_endpoint' => $endpoint,
- '_known_registers' => {}
- };
-
- bless($self, $type);
- return $self;
-}
-
-sub read {
- # Trb->read($address, $len = 1)
- # Reads $len registers starting at $address
- # Returns integer if $len==1 else a reference to an array
-
- my $self = $_[0];
- my $address = $_[1];
- my $len = defined $_[2] ? $_[2] : 1;
-
- if (1 == $len) {
- my $data = $self->{'_data'}->{$address};
- return defined $data ? $data : undef;
- } else {
- my $result = [];
- for(my $i=0; $i < $len; $i++) {
- push $result, $self->read($address + $i);
- }
-
- return $result;
- }
-}
-
-sub write {
- # Trb->write($address, $data)
- # if $data is scalar, it is interpreted as an 32bit integer and
- # written to the register $address
- # if $data is a reference to an array, all entries are sequentially
- # written to the memory area starting at $address;
-
- my $self = $_[0];
- my $address = $_[1];
- my $data = $_[2];
-
- if (not ref $data) {
- $self->{'_data'}->{$address} = $data & 0xFFFFFFFF;
- } elsif (ref $data eq "ARRAY") {
- for(my $i=0; $i < @$data; $i++) {
- $self->write($address + $i, $data->[$i]);
- }
- }
-}
-
-sub loadDump {
- # TrbSim->loadDump($fp)
- # reads in file provided with the file pointer $fp and
- # sets values listed in the input. The following format
- # (used by the trbcmd rm command) is expected:
- #
- # Each line contains one value "0xADDRESS 0xVALUE"
- # Comments after data are allowed and can be sepearated by
- # any non-hex char.
- #
- # All lines not matching this format are skipped.
- my $self = $_[0];
- my $fp = $_[1];
-
- while (my $line = <$fp>) {
- if ($line =~ /^\s*0x([\da-f]+)\s+0x([\da-f]+)/) {
- $self->write(hex($1), hex($2));
- }
- }
-}
-
-sub getEndpoint {
- # TrbSim->getEndpoint()
- # returns id of endpoint simulated
- $_[0]->{'_endpoint'}
-}
-
-sub addKnownRegister {
- # TrbSim->addKnownRegister( $reg )
- # where $reg is a reference to a TrbRegister instance
- my $self = shift;
- my $reg = shift;
-
- $self->{'_known_registers'}{$reg->getAddress} = $reg;
-}
-
-# prefetching is not sensible for simulation.
-# methods are implemented only for compatibility
-sub clearPrefetch {}
-sub addPrefetchRegister {}
-sub prefetch {}
-
-1;
\ No newline at end of file