]> jspc29.x-matter.uni-frankfurt.de Git - daqtools.git/commitdiff
Bugfixes. Throttle support. Edge/Level selection on ITC. Addresses in title
authorhadeshyp <hadeshyp>
Sat, 29 Sep 2012 13:03:50 +0000 (13:03 +0000)
committerhadeshyp <hadeshyp>
Sat, 29 Sep 2012 13:03:50 +0000 (13:03 +0000)
14 files changed:
cts/CtsConfig.pm [new file with mode: 0644]
cts/CtsPlugins/CtsMod00.pm
cts/CtsPlugins/CtsMod50.pm
cts/CtsPlugins/CtsModStatic.pm
cts/cts
cts/cts_gui [new file with mode: 0755]
cts/htdocs/cts.pl
cts/htdocs/index.html
cts/htdocs/layout/base.css
cts/htdocs/layout/gradient.png [deleted file]
cts/htdocs/scripts/base.js
cts/httpi
cts/include/TrbNet.pm
cts/include/TrbSim.pm [deleted file]

diff --git a/cts/CtsConfig.pm b/cts/CtsConfig.pm
new file mode 100644 (file)
index 0000000..553665c
--- /dev/null
@@ -0,0 +1,8 @@
+package CtsConfig;
+
+#default cts endpoint. can be overriden by a command line parameter
+sub getDefaultEndpoint {
+   return 0xf3c0; 
+}
+
+1;
\ No newline at end of file
index d598cbbc9a1d1a4393e2e5830fdc5c6f4835bbaf..89fbe05c6032efea03588d6d17d871ac836fe82a 100755 (executable)
@@ -1,6 +1,7 @@
-# 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;
@@ -11,7 +12,7 @@ use warnings;
 use strict;
 use TrbRegister;
 
-sub moduleName {"Channel Masking"}
+sub moduleName {"Channel Configuration"}
 
 sub init {
    my $self    = $_[0];
@@ -24,7 +25,8 @@ sub init {
 
 # 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',
index f466657070dce4e7e5804fcac513a1af347102e0..f612440cf67e04a1d3c880d080816fa4ea146eb9 100755 (executable)
@@ -1,8 +1,8 @@
 # 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;
 
@@ -25,12 +25,25 @@ sub init {
 
    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
index 3d0c4b2bf0daed2bd65b16937d82f4be5fdac592..45609b4a1013209898adbe5ee4899b34d8cca63d 100755 (executable)
@@ -143,6 +143,17 @@ sub init {
       '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);
diff --git a/cts/cts b/cts/cts
index e0f83c3c26f21341ca68dd1b4c0861293ec0f9e4..726dc725e3731aeb35da060c0ee2eebe699de1ff 100755 (executable)
--- a/cts/cts
+++ b/cts/cts
@@ -11,8 +11,9 @@ use lib "./include";
    
 # 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];
@@ -458,7 +459,9 @@ sub commandMonitor {
                $rate , $value];
          }
       }
-
+      
+      printTable $tab;
+      
       if ($filename) {
       # store json
          my $json = JSON::PP->new->encode({
@@ -470,7 +473,7 @@ sub commandMonitor {
             'monitor' => $monData
          });
          
-         open FH, ">$filename.js";
+         open FH, ">$filename/dump.js";
          print FH $json;
          close FH;
       
@@ -485,7 +488,7 @@ sub commandMonitor {
          
          
          if ($#{ $plotData } > 4) {
-            open FH, ">$filename.gpdata";
+            open FH, ">$filename/plot.data";
             foreach (@{$plotData}) {
                my @row = (@{ $_ });
                $row[0] -= $plotData->[-1][0];
@@ -494,60 +497,51 @@ sub commandMonitor {
             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;
@@ -560,8 +554,6 @@ for(my $i=0; $i < @ARGV; $i++) {
    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) {
@@ -595,11 +587,11 @@ for(my $i=0; $i < @ARGV; $i++) {
       $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") {
@@ -609,7 +601,7 @@ for(my $i=0; $i < @ARGV; $i++) {
       }
       
       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") {
@@ -618,12 +610,12 @@ for(my $i=0; $i < @ARGV; $i++) {
          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 {
diff --git a/cts/cts_gui b/cts/cts_gui
new file mode 100755 (executable)
index 0000000..2cfb686
--- /dev/null
@@ -0,0 +1,29 @@
+#!/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
index 8badb5c73a7c32b885f47c1bcaca7bbfc8d200c8..faeea875abd798492bf76017b5b7cbf6e8a4ed05 100755 (executable)
@@ -4,29 +4,21 @@ use warnings;
 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'};
 
@@ -48,12 +40,17 @@ if ($query eq "init") {
    }
    
    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 {
@@ -63,9 +60,11 @@ if ($query eq "init") {
    
    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";
 }
index 7809436c5c0a001cc1d5925282f71abcb55aa1d7..ba974577afb606ed8f800e0c74499b76fb9f8e13 100644 (file)
@@ -12,6 +12,7 @@
       <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>
@@ -21,7 +22,7 @@
             <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>
@@ -29,7 +30,7 @@
                      <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>
@@ -41,7 +42,7 @@
                      <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>
@@ -51,7 +52,7 @@
                      <td colspan="3">&nbsp;</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">&nbsp;</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"
index b6c2e2a84291db8739f31951502b9b4904539a7e..6a71e8488781ffbac4c98e64cd5091944b268815 100644 (file)
@@ -3,7 +3,7 @@
       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;
@@ -19,7 +19,7 @@
    #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 {
@@ -44,7 +48,7 @@
 
    #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}
diff --git a/cts/htdocs/layout/gradient.png b/cts/htdocs/layout/gradient.png
deleted file mode 100644 (file)
index 7a75acb..0000000
Binary files a/cts/htdocs/layout/gradient.png and /dev/null differ
index 2cee660d209a0b98023978d150195c4277b62579..542a8998af1449c42c9ff4215cd00c77089a6aac 100644 (file)
@@ -1,4 +1,11 @@
+/** 
+ * 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") {
@@ -17,6 +24,41 @@ function parseNum(str) {
    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,
@@ -34,6 +76,7 @@ var CTS = new Class({
       this.renderTriggerInputs();
       this.renderCoins();
       this.renderRegularPulsers();
+      this.renderRandPulsers();
       this.renderCTSDetails();
       
       this.initAutoRates();
@@ -41,11 +84,14 @@ var CTS = new Class({
       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) {
@@ -101,10 +147,14 @@ var CTS = new Class({
       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   
@@ -114,14 +164,19 @@ var CTS = new Class({
 
             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;
             }
 
@@ -132,8 +187,10 @@ var CTS = new Class({
          }.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();
    },
@@ -152,9 +209,7 @@ var CTS = new Class({
             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 = "";
@@ -165,11 +220,7 @@ var CTS = new Class({
             } 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 = "";
@@ -198,28 +249,21 @@ var CTS = new Class({
                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');
@@ -233,7 +277,7 @@ var CTS = new Class({
 
 /**
  * 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.
@@ -246,39 +290,26 @@ var CTS = new Class({
          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');
          
@@ -297,6 +328,29 @@ var CTS = new Class({
          }
       });
    },
+
+/**
+ * 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
@@ -308,9 +362,11 @@ var CTS = new Class({
          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(
@@ -342,8 +398,6 @@ var CTS = new Class({
                      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})
             )
          );
       }
@@ -356,10 +410,11 @@ var CTS = new Class({
  */
    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(
@@ -367,6 +422,16 @@ var CTS = new Class({
                .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 (
@@ -374,12 +439,25 @@ var CTS = new Class({
                .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;
+         };
       };
    },
 
@@ -389,7 +467,7 @@ var CTS = new Class({
  * 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));
@@ -428,7 +506,7 @@ var CTS = new Class({
    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;
       }
       
@@ -478,7 +556,7 @@ var CTS = new Class({
             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);
          }
          
@@ -495,13 +573,64 @@ var CTS = new Class({
          }
          
          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);}
    }
 });
 
@@ -526,7 +655,28 @@ function formatFreq(val, sigDigits) {
    
    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: {},
@@ -599,3 +749,5 @@ var GuiExpander = new Class({
 });
 var guiExpander;
 window.addEvent('domready', function() {guiExpander = new GuiExpander();});
+
+function id(x) {return x;}
\ No newline at end of file
index cea5322d8a08db093d8137ec701229afe262b6d2..a7af467ec339199911c83c355a0aa8a3bcf722de 100755 (executable)
--- a/cts/httpi
+++ b/cts/httpi
@@ -83,6 +83,7 @@ $VERSION = "1.7 (Demonic/Linux)";
 $logfile = "./htdocs/access.log";
 $path = "./htdocs";
 $sockaddr = 'S n a4 x8';
+
 $server_host = $ARGV[0];
 $server_port = $ARGV[1];
 
@@ -109,7 +110,7 @@ EOF
 %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);
index cfc56bfda4b2d5a5d65e52cb73d24c74c03bcd96..eb99be04024cee14738c2e3d23770e9143ceea9c 100644 (file)
@@ -14,7 +14,9 @@ sub new {
    my $self = {
       '_endpoint' => $endpoint,
       '_known_registers' => {},
-      '_prefetch' => {}
+      '_prefetch' => {},
+      '_write_cache' => {},
+      '_cached_writes' => 0
    };
    
    bless($self, $type);
@@ -39,11 +41,19 @@ sub read {
    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);
@@ -52,6 +62,7 @@ sub read {
       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};
@@ -75,6 +86,30 @@ sub addPrefetchRegister {
    $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;
@@ -146,7 +181,11 @@ sub write {
    
    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]);
diff --git a/cts/include/TrbSim.pm b/cts/include/TrbSim.pm
deleted file mode 100644 (file)
index 4c8d898..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-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