#!/usr/bin/perl


$USAGE = "./RiscvUart.pl [-r 0x80000000] |\n" .                  # read word
         "               [-w 0x80000000 0xFFFFFFFF] |\n" .       # write word
         "               [-range 0x80000000 0x80000040] | \n" .  # read range
         "               [-fill ] 0x80000000 0x80000100 0xFFFFFFFF |\n" . # fill
         "               [-d 0x80000000 n_bytes]\n" . # dump n bytes from addr->
         "               [-p ttyUSB0]\n";             # use given port instead



# To release riscv core reset: "./RiscvUart.pl -w 0xF0000000 0x00000001"


$use_port  = &my_opt("-p");



if ( (scalar %{ Device::SerialPort:: }) == 0 ) {
  use lib  qw(perllib/lib/perl5/x86_64-linux-gnu-thread-multi);
  use Device::SerialPort qw( :PARAM :STAT 0.07 );
}
else {
  use Device::SerialPort qw( :PARAM :STAT 0.07 );
}


my $port;
if ($use_port ne "null") {
    $port = Device::SerialPort->new( "/dev/$use_port" );
    print("   NOTE: USING PORT : $use_port\n\n");
}
else {
    $port = Device::SerialPort->new( "/dev/ttyUSB0" );
    print("   NOTE: USING USBUART PORT\n\n");
}


$port->baudrate(1000000);
$port->databits(8);
$port->stopbits(1);
$port->parity("none");



if ( $#ARGV+1 > 0) {
    # -r
    ###    READ SINGLE WORD
    if ( lc($ARGV[0])  eq "-r" && ($#ARGV+1 == 2) ) {
	if ($ARGV[1] =~ /\A0x/) {$ii = hex($ARGV[1])} else {$ii = $ARGV[1]}
	$res = &RiscvUartRd($ii);                    # addr
	printf ("0x%08X -> 0x%08X\n", $ii, $res);
    }
    # -d
    ###    READ RANGE
    elsif ( (lc($ARGV[0])  eq "-r" || lc($ARGV[0])  eq "-range")  &&
	    ($#ARGV+1 == 3)) {
	if ($ARGV[1] =~ /\A0x/) {$start = hex($ARGV[1])} else {$start=$ARGV[1]}
	if ($ARGV[2] =~ /\A0x/) {$end = hex($ARGV[2])} else {$end = $ARGV[2]}
        print "Dumping memory from $start to $end\n";
	for ($ii=$start; $ii<=$end; $ii+=4) {        # addr 1:0 are for BYTES
	    $res = &RiscvUartRd($ii);                # addr
	    printf ("0x%08X -> 0x%08X\n", $ii, $res);
	}
    }
    ###    WRITE SINGLE WORD
    elsif ( lc($ARGV[0])  eq "-w" && ($#ARGV+1 == 3) ) {
	if ($ARGV[1] =~ /\A0x/) {$adr = hex($ARGV[1])} else {$adr = $ARGV[1]}
	if ($ARGV[2] =~ /\A0x/) {$dat = hex($ARGV[2])} else {$dat = $ARGV[2]}
	&RiscvUartWr($adr, $dat);                    # addr, data
    }
    ###    FILL RANGE
    elsif ( (lc($ARGV[0])  eq "-w" || lc($ARGV[0])  eq "-fill")  &&
	    ($#ARGV+1 == 4) ) {
	if ($ARGV[1] =~ /\A0x/) {$start = hex($ARGV[1])} else {$start= $ARGV[1]}
	if ($ARGV[2] =~ /\A0x/) {$end   = hex($ARGV[2])} else {$end  = $ARGV[2]}
	if ($ARGV[2] =~ /\A0x/) {$dat   = hex($ARGV[3])} else {$dat  = $ARGV[3]}
	&RiscvUartWr($adr, $dat);                    # addr, data
	for ($ii=$start; $ii<=$end; $ii+=4) {        # addr [1:0] are for BYTES
	    $res = &RiscvUartWr($ii, $dat);          # addr, data
	}
    }
    ###    DUMP N BYTES
    elsif ( lc($ARGV[0])  eq "-d" && ($#ARGV+1 == 3) ) {
	if ($ARGV[1] =~ /\A0x/) {$ii = hex($ARGV[1])} else {$ii = $ARGV[1]}
	if ($ARGV[2] =~ /\A0x/) {$bb = hex($ARGV[2])} else {$bb = $ARGV[2]}
	if ($bb >= 256) {
	    $bb = 0xFF;                              # max is 255
	    print STDERR "\n  WARNING: Max bytes exceeded, dumping 255\n\n";
	}
	if ( $bb < 1 ) {
	    $bb = 0x01;    # min is 1
	    print STDERR "\n  WARNING: Min bytes must be > 0, dumping 1\n\n";
	}
	$res = &RiscvUartRdNBytes($ii, $bb);         # dump n bytes addr ii ->
	printf ("%s", $res);
    }
    else {
	printf("\nERROR: Bad params, don't no what to do with these\n");
	print "Usage: $USAGE" . "\n";
    }
    exit;
}


exit;




# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub binary2hex {
    my ($d) = @_;
    my ($l,$x,$i,$h,$res);
    $l = length($d);
    # print "LEN: $l\n";
    $x = "x";
    $i = 0;
    my($cnt) = 0;
    for ($i=0; $i<$l; $i++) {
        $x = substr($d,$i,1);
        $h = ord($x);                    # numeric value of char (NOT byte)
        $res .= sprintf("%02X", $h);     # string
        $cnt++;
    }
    return $res;
}


# - - - - - - - - - - - - - - - - - - - - -
#  REQ_WRITE = 0x10,  LEN (in bytes), ADDR0,1,2,3,  DATA0,1,2,3
sub RiscvUartWr {
   my($adr, $dat)   = @_;
   $dat = sprintf ("%08X", $dat);        # hex/dec
   $dat =~ /(\S\S)(\S\S)(\S\S)(\S\S)/;
   $dat = $4 . $3 . $2 . $1;             # Data is send lsb byte first
   my($bytes) = "1004" .                 # 0x10 (wr), 0x04 (len), ADDR32, DATA32
       sprintf ("%08X", $adr) .          # hex/dec
       $dat;
   $bytes = pack("H*", $bytes);          # to binary
   $port->lookclear; 
   $port->write("$bytes");
}



# - - - - - - - - - - - - - - - - - - - - -
#  REQ_READ = 0x11,  LEN, ADDR0,1,2,3
sub RiscvUartRd {
   my($adr)   = @_;
   my($bytes) = "1104" .                 # 0x11 (rd), 0x04 (len), ADDR3
       sprintf ("%08X", $adr);           # to string, hex/dec
   # e.g. : "110480000000"
   $bytes = pack("H*", $bytes);          # to binary
   $port->lookclear; 
   $port->write("$bytes");
   select(undef, undef, undef, 0.010);   # sleep of 10 milliseconds
   my ($numChars, $chars) = $port->read(100);
   $chars = uc(unpack("H*", $chars));
   $chars =~ /(\S\S)(\S\S)(\S\S)(\S\S)/;
   $chars = $4 . $3 . $2 . $1;           # data is send lsb byte first
    if ($verbose > 0) {
     printf ("  0x%08X -> 0X%s  (%d bytes)\n", $adr, $chars, $numChars);
   }
   return hex($chars);
}



# - - - - - - - - - - - - - - - - - - - - -
#  REQ_READ = 0x11,  LEN, ADDR0,1,2,3
sub RiscvUartRdNBytes {
   my($adr, $bcnt)   = @_;
   # e.g.  0x11 (rd), 0x04 (len), ADDR32, DATA32
   my($bytes) = "11" .
       sprintf ("%02X%08X", $bcnt, $adr);  # to string, for hex/dec
   # like: "110480000000"
   $bytes = pack("H*", $bytes);            # to binary
   $port->lookclear; 
   $port->write("$bytes");
   select(undef, undef, undef, 0.010);     # sleep of 10 milliseconds
   my ($tmp_str, $tmp_wrd) = "";
   my ($numChars, $chars);
   my($ii) = 0;
   for ($ii=1; $ii<=$bcnt; $ii++) {
       ($numChars, $chars) = $port->read(1);
       $chars = uc(unpack("H*", $chars));
       $tmp_wrd = sprintf("%02X", hex($chars)) . $tmp_wrd;
       if ($ii%4 == 0) {
	   $tmp_str .=  sprintf ("0x%08X -> ", $adr) . "0x$tmp_wrd\n";
	   $adr += 4;
	   $tmp_wrd = "";
       }
   }
   if ( $tmp_wrd ne "" ) {
       $tmp_str .=  sprintf ("0x%08X -> ", $adr) . "0x$tmp_wrd\n";
   }
   $tmp_wrd = "";
   return $tmp_str;
}



# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


sub my_opt{
    my($t) = (@_);
    my($i);
    my($res) = "null";
    for($i=0;$i<=$#ARGV;$i++){
	if ($ARGV[$i] eq $t){
	    last;
	}
    }
    if ($ARGV[$i] eq $t) {
	$res = $ARGV[$i+1];
	for(;$i+2<=$#ARGV;$i++){
	    $ARGV[$i] = $ARGV[$i+2];
	}
	pop @ARGV;
	pop @ARGV;
    }
    return $res;
}


sub die_usage{
    my($p) = @_;
    print STDERR "$p\n";
    die "$USAGE\n";
}


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
