# $Id: util.tcl 1177 2019-06-30 12:34:07Z mueller $
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright 2011-2019 by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
#
#  Revision History:
# Date         Rev Version  Comment
# 2019-03-02  1116   1.3.1  define INIT
# 2017-06-19   914   1.3    17bit support; use sstat(awidth); add isnarrow
# 2017-04-22   883   1.2.1  setup: now idempotent
# 2016-07-09   784   1.2    22bit support: mask sstat(wide); add iswide
# 2015-04-03   661   1.1    drop estatdef (stat err check default now)
# 2014-08-14   582   1.0.1  add srun* procs; add nscmd and tout variables
# 2014-08-10   581   1.0    Initial version
# 2011-07-03   387   0.1    Frist draft
#

package provide tst_sram 1.0

package require rutiltpp
package require rutil
package require rlink

namespace eval tst_sram {
  # name space variables
  #
  variable nscmd 0;             # length of current sequencer command list
  variable tout 10.;            # default time out
  variable iswide   -1;         # sstat.awidth=22 cache
  variable isnarrow -1;         # sstat.awidth=17 cache
  #
  # setup register descriptions for tst_sram core design ---------------------
  # 
  regdsc INIT  {mem 1} {seq 0}; # for rbus init against base addr
  regdsc MCMD  {ld 14} {inc 13} {we 12} {be 11 4} {addrh 5 6}
  regdsc SSTAT {awidth 15 3} {wswap 9} {wloop 8} \
               {loop 7} {xord 6} {xora 5} {veri 4} {fail 1} {run 0}
  regdsc SCMD  {wait 31 4} {we 24} {be 23 4} {addr 17 18}
  #
  # setup: amap definitions for tst_sram core design -------------------------
  # 
  proc setup {{base 0x0000}} {
    if {[rlc amap -testname sr.mdih $base]} {return}
    rlc amap -insert sr.mdih   [expr {$base + 0x00}]
    rlc amap -insert sr.mdil   [expr {$base + 0x01}]
    rlc amap -insert sr.mdoh   [expr {$base + 0x02}]
    rlc amap -insert sr.mdol   [expr {$base + 0x03}]
    rlc amap -insert sr.maddrh [expr {$base + 0x04}]
    rlc amap -insert sr.maddrl [expr {$base + 0x05}]
    rlc amap -insert sr.mcmd   [expr {$base + 0x06}]
    rlc amap -insert sr.mblk   [expr {$base + 0x07}]
    rlc amap -insert sr.slim   [expr {$base + 0x08}]
    rlc amap -insert sr.saddr  [expr {$base + 0x09}]
    rlc amap -insert sr.sblk   [expr {$base + 0x0a}]
    rlc amap -insert sr.sblkc  [expr {$base + 0x0b}]
    rlc amap -insert sr.sblkd  [expr {$base + 0x0c}]
    rlc amap -insert sr.sstat  [expr {$base + 0x0d}]
    rlc amap -insert sr.sstart [expr {$base + 0x0e}]
    rlc amap -insert sr.sstop  [expr {$base + 0x0f}]
    rlc amap -insert sr.seaddr [expr {$base + 0x10}]
    rlc amap -insert sr.sedath [expr {$base + 0x11}]
    rlc amap -insert sr.sedatl [expr {$base + 0x12}]
  }
  #
  # init: reset tst_sram -----------------------------------------------------
  # 
  proc init {} {
    rlc exec \
      -wreg sr.sstop 1 \
      -wreg sr.sstat 0
  }
  #
  # checkawidth: inspect SSTAT(awidth) ---------------------------------------
  # 
  proc checkawidth {} {
    variable iswide
    variable isnarrow
    rlc exec -rreg sr.sstat sstat
    set awidth [regget tst_sram::SSTAT(awidth) $sstat]
    set iswide   [expr {$awidth + 16 == 22}]
    set isnarrow [expr {$awidth + 16 == 17}]
    return ""
  }
  #
  # iswide: 1 if 22bit system ------------------------------------------------
  # 
  proc iswide {} {
    variable iswide
    if {$iswide < 0} { checkawidth }
    return $iswide
  }
  #
  # isnarrow: 1 if 17bit system ----------------------------------------------
  # 
  proc isnarrow {} {
    variable isnarrow
    if {$isnarrow < 0} { checkawidth }
    return $isnarrow
  }
  #
  # scmd_write: write a scmd list --------------------------------------------
  # 
  proc scmd_write {scmdlist} {
    variable nscmd
    set buf {}
    set nscmd 0
    rlc exec -wreg sr.saddr 0

    foreach scmditem $scmdlist {
      set wait [lindex $scmditem 0]
      set wec  [lindex $scmditem 1]
      set bec  [lindex $scmditem 2]
      set addr [lindex $scmditem 3]
      set mval [lindex $scmditem 4]
      set we   [expr {($wec eq "w") ? 1 : 0}]
      set scmd [regbld tst_sram::SCMD \
                  [list wait $wait] \
                  [list we $we] \
                  [list be [bvi b $bec]] \
                  [list addr $addr] ]
      set scmdh [expr {($scmd>>16) & 0xffff}]
      set scmdl [expr { $scmd      & 0xffff}]
      set mvalh [expr {($mval>>16) & 0xffff}]
      set mvall [expr { $mval      & 0xffff}]
      lappend buf $scmdh $scmdl $mvalh $mvall
      if {[llength $buf] == 256} {
        rlc exec -wblk sr.sblk $buf
        set buf {}
      }
      incr nscmd
    }
    if {[llength $buf] > 0} {
      rlc exec -wblk sr.sblk $buf
    }
    return
  }

  #
  # scmd_read:  read a scmd list ---------------------------------------------
  # 
  proc scmd_read {length} {
    set scmdlist {}
    if {$length == 0} {return $scmdlist}

    rlc exec -rreg sr.saddr saddr_save \
             -wreg sr.saddr 0
    while {$length > 0} {
      set chunk $length
      if {$chunk > 64} {set chunk 64}
      set length [expr {$length - $chunk}]
      rlc exec -rblk sr.sblk [expr {4*$chunk}] buf
      foreach {scmdh scmdl mvalh mvall} $buf {
        set scmd [expr {($scmdh<<16) | $scmdl}]
        set mval [expr {($mvalh<<16) | $mvall}]
        set wait [regget tst_sram::SCMD(wait) $scmd]
        set we   [regget tst_sram::SCMD(we)   $scmd]
        set be   [regget tst_sram::SCMD(be)   $scmd]
        set addr [regget tst_sram::SCMD(addr) $scmd]
        set wec  [expr {($we) ? "w" : "r"}]
        set bec  [pbvi b4 $be]
        lappend scmdlist [list $wait $wec $bec $addr $mval]
      }
    }
    rlc exec -wreg sr.saddr $saddr_save
    return $scmdlist
  }

  #
  # scmd_print:  print a scmd list -------------------------------------------
  # 
  proc scmd_print {scmdlist} {
    set rval " ind: dly we   be      addr        mval"
    set ind 0
   foreach scmditem $scmdlist {
     set wait [lindex $scmditem 0]
     set wec  [lindex $scmditem 1]
     set bec  [lindex $scmditem 2]
     set addr [lindex $scmditem 3]
     set mval [lindex $scmditem 4]
     append rval "\n"
     append rval [format "%4d:  %2d  %s %s  0x%6.6x  0x%8.8x" \
                    $ind $wait $wec $bec $addr $mval]
     incr ind
    }
    return $rval
  }

  #
  # srun: single pass run of sequencer ---------------------------------------
  # 
  proc srun {mdih mdil maddrh maddrl {tout 0.}} {
    variable nscmd
    if {$tout == 0}  {set tout $tst_sram::tout}
    if {$nscmd == 0} {error "no or empty scmd list loaded"}
    set sm [rutil::com16 [regbld tst_sram::SSTAT {awidth -1}]]
    rlc exec -init 0 1
    rlc exec -wreg sr.sstat [regbld tst_sram::SSTAT xord xora veri] \
             -wreg sr.mdih   $mdih \
             -wreg sr.mdil   $mdil \
             -wreg sr.maddrh $maddrh \
             -wreg sr.maddrl $maddrl \
             -wreg sr.slim   [expr {$nscmd-1}] \
             -wreg sr.sstart 0x0000
    rlc wtlam $tout
    rlc exec -attn -edata 0x0001
    rlc exec -rreg sr.sstat  -edata [regbld tst_sram::SSTAT xord xora veri] $sm \
             -rreg sr.seaddr -edata 0x0000 \
             -rreg sr.sedath -edata 0x0000 \
             -rreg sr.sedatl -edata 0x0000
    return
  }
  #
  # srun_lists: call srun for mdi and maddr lists ----------------------------
  # 
  proc srun_lists {lmdi lmaddr {tout 0.}} {
    foreach {maddrh maddrl} $lmaddr {
      foreach {mdih mdil} $lmdi {
        srun $mdih $mdil $maddrh $maddrl $tout
      }
    }
    return
  }
  #
  # srun_loop: full maddr* loop of sequencer ---------------------------------
  # 
  proc srun_loop {mdih mdil maddrh maddrl {wide 0} {tout 0.}} {
    variable nscmd
    if {$tout == 0}  {set tout $tst_sram::tout}
    if {$nscmd == 0} {error "no or empty scmd list loaded"}
    set sm [rutil::com16 [regbld tst_sram::SSTAT {awidth -1}]]
    set sstat [regbldkv tst_sram::SSTAT wswap $wide wloop $wide loop 1 \
                                        xord 1 xora 1 veri 1]
    rlc exec -init 0 1
    rlc exec -wreg sr.sstat  $sstat  \
             -wreg sr.mdih   $mdih   \
             -wreg sr.mdil   $mdil   \
             -wreg sr.maddrh $maddrh \
             -wreg sr.maddrl $maddrl \
             -wreg sr.slim   [expr {$nscmd-1}] \
             -wreg sr.sstart 0x0000

    set tbeg [clock milliseconds]
    rlc wtlam $tout
    set tend [clock milliseconds]

    rlc exec -attn -edata 0x0001
    rlc exec -rreg sr.sstat  -edata $sstat $sm \
             -rreg sr.seaddr -edata 0x0000 \
             -rreg sr.sedath -edata 0x0000 \
             -rreg sr.sedatl -edata 0x0000

    set trun [expr {($tend-$tbeg)/1000.}]
    set line [format "loop done maddr=%2.2x %4.4x mdi=%4.4x %4.4x in %7.2f s" \
                $maddrh $maddrl $mdih $mdil $trun]
    rlc log $line
    return
  }
}

