Commit a8ef18e0 authored by Lucas Russo's avatar Lucas Russo

scripts/*: add scripts for jtag tapping on lm32

parent 69b531e4
# JTAG Communication Functions
# Abstracts the JTAG interface away to a few interface functions
# User API Functions
# These should be generic and be the same no matter what the underlying FPGA is.
# Use these to interact with the FPGA.
# TODO: These are designed to assume a single FPGA. Re-design to handle multiple FPGAs, assigning
# an arbitrary ID to each FPGA.
# Import Xilinx ChipScope Engine Interface
if {[info exists env(XIL_CSE_TCL)]} {
if {[string length $env(XIL_CSE_TCL)] > 0} {
puts "Sourcing from XIL_CSE_TCL: $env(XIL_CSE_TCL) ..."
source $env(XIL_CSE_TCL)/csejtag.tcl
source $env(XIL_CSE_TCL)/csefpga.tcl
source $env(XIL_CSE_TCL)/csecore.tcl
source $env(XIL_CSE_TCL)/csevio.tcl
} else {
puts "Sourcing from XILINX: $env(XILINX)/cse/tcl ..."
source $env(XILINX)/cse/tcl/csejtag.tcl
source $env(XILINX)/cse/tcl/csefpga.tcl
source $env(XILINX)/cse/tcl/csecore.tcl
source $env(XILINX)/cse/tcl/csevio.tcl
}
} else {
puts "Sourcing from XILINX: $env(XILINX)/cse/tcl ..."
source $env(XILINX)/cse/tcl/csejtag.tcl
source $env(XILINX)/cse/tcl/csefpga.tcl
source $env(XILINX)/cse/tcl/csecore.tcl
source $env(XILINX)/cse/tcl/csevio.tcl
}
namespace import ::chipscope::*
# Manually load the files
source /usr/lib/tclx8.4/tcllib.tcl
source /usr/lib/tclx8.4/tclx.tcl
source /usr/lib/tclx8.4/arrayprocs.tcl
source /usr/lib/tclx8.4/edprocs.tcl
source /usr/lib/tclx8.4/help.tcl
source /usr/lib/tclx8.4/showproc.tcl
source /usr/lib/tclx8.4/autoload.tcl
source /usr/lib/tclx8.4/events.tcl
source /usr/lib/tclx8.4/pkgIndex.tcl
source /usr/lib/tclx8.4/stringfile.tcl
source /usr/lib/tclx8.4/buildhelp.tcl
source /usr/lib/tclx8.4/fmath.tcl
source /usr/lib/tclx8.4/profrep.tcl
source /usr/lib/tclx8.4/compat.tcl
source /usr/lib/tclx8.4/forfile.tcl
source /usr/lib/tclx8.4/pushd.tcl
source /usr/lib/tclx8.4/convlib.tcl
source /usr/lib/tclx8.4/globrecur.tcl
source /usr/lib/tclx8.4/setfuncs.tcl
# Locate the library
package ifneeded Tclx 8.4
load /usr/lib/libtclx8.4.so Tclx
# Load Tclx
package require Tclx
# Now the command is defined
#auto_load_file tcllib.tcl
#source /usr/lib/tclx8.4/arrayprocs.tcl
#source /usr/lib/tclx8.4/edprocs.tcl
#source /usr/lib/tclx8.4/help.tcl
#source /usr/lib/tclx8.4/showproc.tcl
#source /usr/lib/tclx8.4/autoload.tcl
#source /usr/lib/tclx8.4/events.tcl
#source /usr/lib/tclx8.4/pkgIndex.tcl
#source /usr/lib/tclx8.4/stringfile.tcl
#source /usr/lib/tclx8.4/buildhelp.tcl
#source /usr/lib/tclx8.4/fmath.tcl
#source /usr/lib/tclx8.4/profrep.tcl
#source /usr/lib/tclx8.4/tcllib.tcl
#source /usr/lib/tclx8.4/compat.tcl
#source /usr/lib/tclx8.4/forfile.tcl
#source /usr/lib/tclx8.4/pushd.tcl
#source /usr/lib/tclx8.4/tclx.tcl
#source /usr/lib/tclx8.4/convlib.tcl
#source /usr/lib/tclx8.4/globrecur.tcl
#source /usr/lib/tclx8.4/setfuncs.tcl
#namespace import ::tclx::*
# Initialize the FPGA
proc fpga_init {} {
global fpga_name
global jtag_handle
global fpga_deviceIndex
set jtag_handle [csejtag_session create "jtagWriteMessage"]
set fpga_deviceIndex [find_target_fpga]
if {$fpga_deviceIndex == -1} {
return -1
}
return 0
}
proc fpga_cleanup {} {
global jtag_handle
csejtag_target close $jtag_handle
csejtag_session destroy $jtag_handle
}
# Push new work to the FPGA
#proc push_work_to_fpga {workl} {
# global jtag_handle
# global fpga_deviceIndex
# global CSEJTAG_LOCKED_ME
# global CSE_MSG_ERROR
# global CSEJTAG_SHIFT_READWRITE
# global CSEJTAG_RUN_TEST_IDLE
# global USER_NUM
#
# array set work $workl
#
# set midstate [reverseHex $work(midstate)]
# set data [string range [reverseHex $work(data)] 104 127]
# set hexdata "${midstate}${data}"
#
# set cablelock [csejtag_target lock $jtag_handle $TIMEOUT]
# if {$cablelock != $CSEJTAG_LOCKED_ME} {
# csejtag_session send_message $jtag_handle $CSE_MSG_ERROR "cse_lock_target failed"
# csejtag_target close $jtag_handle
# return
# }
#
# if {[catch {
#
# csejtag_tap shift_device_ir $jtag_handle $fpga_deviceIndex $CSEJTAG_SHIFT_READWRITE $CSEJTAG_RUN_TEST_IDLE 0 $device_ir_length $USER_NUM
#
# for {set i 0} {$i < 45} {incr i} {
# if {$i >= 44} {
# set tx_data "FF"
# } else {
# set tx_data [string range $hexdata [expr {$i*2}] [expr {$i*2+1}]]
# }
#
# if {$i == 0} {
# set tx_data "00$tx_data"
# } else {
# set tx_data "01$tx_data"
# }
#
# #puts "SEND: $tx_data"
#
# set rx_data [csejtag_tap shift_device_dr $jtag_handle $fpga_deviceIndex $CSEJTAG_SHIFT_READWRITE $CSEJTAG_RUN_TEST_IDLE 0 11 $tx_data]
#
# puts "RECV: $rx_data"
# }
#
# } result]} {
# global errorCode
# global errorInfo
# puts stderr "\nCaught error: $result"
# puts stderr "**** Error Code ***"
# puts stderr $errorCode
# puts stderr "**** Tcl Trace ****"
# puts stderr $errorInfo
# puts stderr "*******************"
# }
#
# csejtag_target unlock $jtag_handle
#}
# Clear all work on the FPGA
proc clear_fpga_work {} {
# Currently does nothing, since there is no work queue
}
# Get a new result from the FPGA if one is available. Returns Golden Nonce (integer).
# If no results are available, returns -1
#proc get_result_from_fpga {} {
# global jtag_handle
# global fpga_deviceIndex
# global CSEJTAG_LOCKED_ME
# global CSE_MSG_ERROR
# global CSEJTAG_SHIFT_READWRITE
# global CSEJTAG_RUN_TEST_IDLE
# global USER_NUM
#
# set golden_nonce -1
#
# # Lock the cable
# set cablelock [csejtag_target lock $jtag_handle 5000]
# if {$cablelock != $CSEJTAG_LOCKED_ME} {
# csejtag_session send_message $jtag_handle $CSE_MSG_ERROR "cse_lock_target failed"
# csejtag_target close $jtag_handle
# return -1
# }
#
# if {[catch {
#
# csejtag_tap shift_device_ir $jtag_handle $fpga_deviceIndex $CSEJTAG_SHIFT_READWRITE $CSEJTAG_RUN_TEST_IDLE 0 $device_ir_length $USER_NUM
#
# for {set i 0} {$i < 4} {incr i} {
# set tx_data "0000"
#
# set rx_data [csejtag_tap shift_device_dr $jtag_handle $fpga_deviceIndex $CSEJTAG_SHIFT_READWRITE $CSEJTAG_RUN_TEST_IDLE 0 11 $tx_data]
#
# #puts "RECV: $rx_data"
# set rx_data [expr 0x$rx_data]
#
# if {$rx_data >= 0x1000} {
# if {$golden_nonce == -1} {
# set golden_nonce 0
# }
#
# set golden_nonce [expr {$golden_nonce | (($rx_data & 0xFF) << ($i * 8))}]
# } else {
# set golden_nonce -1
# break
# }
# }
# } result]} {
# global errorCode
# global errorInfo
# puts stderr "\nCaught error: $result"
# puts stderr "**** Error Code ***"
# puts stderr $errorCode
# puts stderr "**** Tcl Trace ****"
# puts stderr $errorInfo
# puts stderr "*******************"
# }
#
# # Unlock the cable
# csejtag_target unlock $jtag_handle
#
# return $golden_nonce
#}
# Return the current nonce the FPGA is on.
# This can be sampled to calculate how fast the FPGA is running.
# Returns -1 if that information is not available.
proc get_current_fpga_nonce {} {
return -1
#if { [instance_exists NONC] } {
# set nonce [read_instance NONC]
# return [expr 0x$nonce]
#} else {
# return -1
#}
}
# Return the FPGA's "name", which could be anything but is hopefully helpful (to the user) in
# indentifying which FPGA the software is talking to.
proc get_fpga_name {} {
global fpga_name
return $fpga_name
}
###
# Internal FPGA/JTAG APIs are below
# These should not be accessed outside of this script
###################################
set fpga_name "Unknown"
set fpga_deviceIndex -1
set jtag_handle 0
set device_ir_length 0
set TIMEOUT 5000
# Parallel IV Cable
set PARALLEL_CABLE_ARGS [list "port=LPT1" \
"frequency=2500000"]
# "frequency=5000000 | 2500000 | 1250000 | 625000 | 200000"
# Platform USB Cable
set PLATFORM_USB_CABLE_ARGS [list "port=USB2" \
"frequency=3000000"]
# frequency="12000000 | 6000000 | 3000000 | 1500000 | 750000"
set CABLE_NAME $CSEJTAG_TARGET_PLATFORMUSB
set CABLE_ARGS $PLATFORM_USB_CABLE_ARGS
# Create global variables
#set ILA_STATUS_WORD_BIT_LEN 512
# BSCAN_VIRTEX6 is set for USER2, which is JTAG opcode 1111000011
# Virtex-6 USER1 Code
set USER_NUM 963
# Set the FPGA device number here
set TARGET_FPGA "XC6VLX240T"
# Try to find an FPGA on the JTAG chain that has mining firmware loaded into it.
# TODO: Return multiple FPGAs if more than one are found (that have mining firmware).
proc find_target_fpga {} {
global jtag_handle
global CABLE_NAME
global CABLE_ARGS
global CSE_MSG_ERROR
global CSE_MSG_INFO
global CSEJTAG_SCAN_DEFAULT
global CSEJTAG_LOCKED_ME
csejtag_target open $jtag_handle $CABLE_NAME 0 $CABLE_ARGS
csejtag_session send_message $jtag_handle $CSE_MSG_INFO "Cable opened successfully.\n"
set cablelock [csejtag_target lock $jtag_handle 5000]
if {$cablelock != $CSEJTAG_LOCKED_ME} {
csejtag_session send_message $jtag_handle $CSE_MSG_ERROR "ERROR: cse_lock_target Failed."
csejtag_target close $jtag_handle
return -1
}
csejtag_session send_message $jtag_handle $CSE_MSG_INFO "Cable Locked.\n"
set validIndex -1
if {[catch {
csejtag_tap autodetect_chain $jtag_handle $CSEJTAG_SCAN_DEFAULT
set deviceCount [csejtag_tap get_device_count $jtag_handle]
for {set deviceIndex 0} {$deviceIndex < $deviceCount} {incr deviceIndex} {
if {[check_if_fpga_is_target $jtag_handle $deviceIndex] == 1} {
set validIndex $deviceIndex
break
}
}
} result]} {
global errorCode
global errorInfo
puts stderr "\nCaught error: $result"
puts stderr "**** Error Code ***"
puts stderr $errorCode
puts stderr "**** Tcl Trace ****"
puts stderr $errorInfo
puts stderr "*******************"
}
csejtag_target unlock $jtag_handle
if {$validIndex != -1} {
return $validIndex
}
puts stderr "ERROR: There are no Xilinx FPGAs with correct firmware loaded on them."
puts stderr "Please program your FPGA with correct firmware and re-run this script.\n"
return -1
}
# Check if the specified FPGA is loaded with miner firmware
proc check_if_fpga_is_target {jtag_handle deviceIndex} {
global fpga_name
global device_ir_length
global TARGET_FPGA
set idcodeBuffer [csejtag_tap get_device_idcode $jtag_handle $deviceIndex]
#set irLength 0
#set irLength [csejtag_db get_irlength_for_idcode $jtag_handle $idcodeBuffer]
set irLength [csejtag_db get_irlength_for_idcode $jtag_handle $idcodeBuffer]
if {$irLength == 0} {
return 0
}
set device_ir_length $irLength
set deviceName [csejtag_db get_device_name_for_idcode $jtag_handle $idcodeBuffer]
if {[string compare $deviceName $TARGET_FPGA] != 0} {
return 0
}
set fpga_name $deviceName
return 1
}
proc jtagWriteMessage {handle msgFlags msg} {
global CSE_MSG_ERROR
global CSE_MSG_WARNING
global CSE_MSG_STATUS
global CSE_MSG_INFO
global CSE_MSG_NOISE
global CSE_MSG_DEBUG
if {[expr ($msgFlags != $CSE_MSG_DEBUG)]} {
if {$msgFlags == $CSE_MSG_ERROR} {
puts -nonewline "Error:"
} elseif {$msgFlags == $CSE_MSG_WARNING} {
puts -nonewline "Warning:"
} elseif {$msgFlags == $CSE_MSG_INFO} {
puts -nonewline "Info:"
} elseif {$msgFlags == $CSE_MSG_STATUS} {
puts -nonewline "Status:"
} elseif {$msgFlags == $CSE_MSG_DEBUG} {
puts -nonewline "Debug:"
}
puts -nonewline $msg
flush stdout
}
}
#! /bin/bash
# \
export RLWRAP_ #\
exec rlwrap -C lm32-ctl-xil -I xtclsh "$0" "$@"
##
#
# Copyright (c) 2011 fpgaminer@bitcoin-mining.com
#
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##
## TODO: Long polling.
## TODO: --verbose option for debugging issues.
## TODO: Handle multiple FPGAs at once.
# Modified by Lucas Russo <lucas.russo@lnls.br> with additions from
#
# lm32-ctl script: http://www.ohwr.org/projects/lm32/repository
source utils.tcl
#source json_rpc.tcl
source jtag_comm.tcl
source lm32_comm.tcl
proc say_line {msg} {
set t [clock format [clock seconds] -format "%D %T"]
puts "\[$t\] $msg"
}
proc say_error {msg} {
set t [clock format [clock seconds] -format "%D %T"]
puts stderr "\[$t\] $msg"
}
proc say_status {rate est_rate accepted rejected} {
set submitted [expr {$rejected + $accepted}]
if {$submitted == 0} {
set rej_rate [expr {$rejected * 100.0}]
} else {
set rej_rate [expr {$rejected * 100.0 / $submitted}]
}
say_line [format "%.2f MH/s (~%.2f MH/s) \[Rej: %i/%i (%.2f%%)\]" $rate $est_rate $rejected $submitted $rej_rate]
}
################################# MAIN #################################
proc main {argc argv} {
global jtag_handle
global CSE_MSG_INFO
puts " --- FPGA Mining Tcl Script --- \n\n"
puts "Looking for and preparing FPGAs...\n"
if {[fpga_init] == -1} {
puts stderr "No mining FPGAs found."
puts "\n\n --- Shutting Down --- \n\n"
exit
}
set fpga_name [get_fpga_name]
puts "Target FPGA Found: $fpga_name\n\n"
#if {[get_current_fpga_nonce] == -1} {
# puts "WARNING: The FPGA's mining firmware does not report a hashrate. Status messages will show 0.00 MH/s, but the FPGA should still be running. Check the estimated rate for approximate hashing rate after shares have been submitted.\n\n"
#}
# Waits input from user and executs specified command
mainLoop $argc $argv
# Close everything
csejtag_target close $jtag_handle
csejtag_session send_message $jtag_handle $CSE_MSG_INFO "Closed cable successfully\n"
csejtag_session destroy $jtag_handle
puts "\n\n --- Shutting Down --- \n\n"
}
# Start the program
main $argc $argv
# mostly from lm32-ctl script: http://www.ohwr.org/projects/lm32/repository
########################### MAIN USER LOOP #########################
proc mainLoop {argc argv} {
sync
set nextcmd $argv
set cmd ""
set line ""
while {$cmd != "quit"} {
puts $argv
if {$argv eq ""} {
set interactive 1
puts -nonewline "\nlm32> "
if {[gets stdin line] < 0} break
} else {
set interactive 0
set line $nextcmd
}
set parts [split $line " "]
set args [lassign $parts cmd]
#set cmd [lindex $parts 0]
#set parts [lreplace $parts 0 0]
#foreach {args} $parts {break}
#set args $parts
#set args $parts[0]
#set parts [lreplace $parts 0 0]
#foreach {arg1 arg2 arg3 arg4} $args {break}
set tail [lassign $args arg1 arg2 arg3 arg4]
switch $cmd {
"" { }
"break" { cpu_break }
"console" { jtag_uart_console }
"reset" { reset }
"sync" { sync }
"read" { set nextcmd [read_memory $arg1 $arg2] }
"dump" { set nextcmd [dump_memory $arg1 $arg2] }
"verify" {verify $arg1}
"write" { write_memory $arg1 $arg2 }
"csr" { csr $arg1 $arg2 }
"recv" { recv }
"send" { send $args }
"load" { load $arg1 }
"quit" { }
default { puts "Unknown command" }
}
if {$interactive == 0} {
break
}
}
# Close device
}
########################### LOW LEVEL ACCESS #########################
#proc jtag_put {handle deviceIndex val} {
proc jtag_put {val} {
global jtag_handle
global fpga_deviceIndex
global device_ir_length
global CSEJTAG_LOCKED_ME
global CSE_MSG_ERROR
global CSEJTAG_SHIFT_READWRITE
global CSEJTAG_SHIFT_WRITE
global CSEJTAG_RUN_TEST_IDLE
global USER_NUM
set ret 0
if {[catch {
csejtag_tap shift_device_ir $jtag_handle $fpga_deviceIndex $CSEJTAG_SHIFT_READWRITE $CSEJTAG_RUN_TEST_IDLE 0 $device_ir_length "[format %03X $USER_NUM]"
#csejtag_tap shift_device_dr $handle $deviceIndex $CSEJTAG_SHIFT_WRITE $CSEJTAG_SHIFT_DR 0 11 "[format %03X $val]"
#csejtag_tap shift_device_dr $HANDLE $DEVICE_INDEX $CSEJTAG_SHIFT_WRITE $CSEJTAG_SHIFT_DR 0 11 "[format %03X $val]"
set ret 0x[csejtag_tap shift_device_dr $jtag_handle $fpga_deviceIndex $CSEJTAG_SHIFT_READWRITE $CSEJTAG_RUN_TEST_IDLE 0 11 "[format %03X $val]"]
#device_virtual_dr_shift -instance_index 0 -length 11 -dr_value "[format %03X $val]" -value_in_hex -no_captured_dr_value
} result]} {
global errorCode
global errorInfo
puts stderr "\nCaught error: $result"
puts stderr "**** Error Code ***"
puts stderr $errorCode
puts stderr "**** Tcl Trace ****"
puts stderr $errorInfo
puts stderr "*******************"
}
return $ret
}
#proc jtag_get {handle deviceIndex} {
proc jtag_get {} {
global jtag_handle
global fpga_deviceIndex
global device_ir_length
global CSEJTAG_LOCKED_ME
global CSE_MSG_ERROR
global CSEJTAG_SHIFT_READWRITE
global CSEJTAG_SHIFT_READ
global CSEJTAG_RUN_TEST_IDLE
global USER_NUM
set ret 0
if {[catch {
csejtag_tap shift_device_ir $jtag_handle $fpga_deviceIndex $CSEJTAG_SHIFT_READWRITE $CSEJTAG_RUN_TEST_IDLE 0 $device_ir_length "[format %03X $USER_NUM]"
#return 0x[csejtag_tap shift_device_dr $HANDLE $DEVICE_INDEX $CSEJTAG_SHIFT_READ $CSEJTAG_SHIFT_DR 0 11 "[format %03X 000]"]
set ret 0x[csejtag_tap shift_device_dr $jtag_handle $fpga_deviceIndex $CSEJTAG_SHIFT_READ $CSEJTAG_RUN_TEST_IDLE 0 11 "[format %03X 000]"]
#return 0x[device_virtual_dr_shift -instance_index 0 -length 11 -dr_value 000 -value_in_hex]
} result]} {
global errorCode
global errorInfo
puts stderr "\nCaught error: $result"
puts stderr "**** Error Code ***"
puts stderr $errorCode
puts stderr "**** Tcl Trace ****"
puts stderr $errorInfo
puts stderr "*******************"
}
return $ret
}
#proc jtag_lock {handle deviceIndex timeout} {
proc jtag_lock {} {
global jtag_handle
global CSEJTAG_LOCKED_ME
global TIMEOUT
set cablelock [csejtag_target lock $jtag_handle $TIMEOUT]
if {$cablelock != $CSEJTAG_LOCKED_ME} {
csejtag_session send_message $jtag_handle $CSE_MSG_ERROR "cse_lock_target failed"
csejtag_target close $jtag_handle
#return
}
return $cablelock
#csejtag_tap shift_device_ir $HANDLE $DEVICE_INDEX $CSEJTAG_SHIFT_WRITE $CSEJTAG_RUN_TEST_IDLE 0 1 "[format %01X 1]"
#device_lock -timeout 10000
#device_virtual_ir_shift -instance_index 0 -ir_value 1 -no_captured_ir_value
# -show_equivalent_device_ir_dr_shift
}
#proc jtag_unlock {handle} {
proc jtag_unlock {} {
global jtag_handle
csejtag_target unlock $jtag_handle
}
############################ SAFE-ISHL ACCESS #########################
# 000 1111 0000
# 111 1000 0000
proc jtag_val {idx val} {
set v [ expr { ($val << 3) | $idx }]
jtag_put "$v"
}
# 000 1111 0000
proc jtag_cmd {idx cmd} {
set val [ expr { $cmd<<4 }]
jtag_val "$idx" "$val"
}
proc jtag_low {i} {
set high 1
while {$high >= 1} {
set val [jtag_get]
set high [expr {($val >> $i) & 1}]
}
return [expr {$val >> 3}]
}
proc jtag_high {i} {
set high 0
while {$high < 1} {
set val [jtag_get]
set high [expr {($val >> $i) & 1}]
}
return [expr {$val >> 3}]
}
############################## COMMANDS ###############################
proc jtag_read_addr {addr} {
jtag_cmd 0 1
jtag_val 0 "[expr {($addr >> 24) & 0xff}]"
jtag_val 0 "[expr {($addr >> 16) & 0xff}]"
jtag_val 0 "[expr {($addr >> 8) & 0xff}]"
jtag_val 0 "[expr {($addr >> 0) & 0xff}]"
return [jtag_low 2]
}
proc jtag_read_next {} {
jtag_cmd 0 3
return [jtag_low 2]
}
proc jtag_read_memory {addr len} {
set out [list]
for {set i 0} {$i < $len} {incr i} {
set x [expr {$addr+$i}]
if {$i == 0 || ($x & 0xffff) == 0} {
lappend out "[format %02X [jtag_read_addr $x]]"
} else {
lappend out "[format %02X [jtag_read_next]]"
}
}
return "$out"
}
proc jtag_write_addr {addr val} {
jtag_cmd 0 2
jtag_val 0 "[expr {($addr >> 24) & 0xff}]"
jtag_val 0 "[expr {($addr >> 16) & 0xff}]"
jtag_val 0 "[expr {($addr >> 8) & 0xff}]"
jtag_val 0 "[expr {($addr >> 0) & 0xff}]"
jtag_val 0 "$val"
return [jtag_low 2]
}
proc jtag_write_next {val} {
jtag_cmd 0 4
jtag_val 0 "$val"
# return [jtag_low 2]
}
proc jtag_write_memory {addr data} {
set first 1
foreach j $data {
if {$first == 1 || ($addr & 0xffff) == 0} {
set first 0
jtag_write_addr "$addr" "$j"
} else {
jtag_write_next "$j"
}
set addr [expr {$addr + 1}]
}
}
proc jtag_uart_write {val} {
jtag_low 1
jtag_val 1 "$val"
}
proc jtag_uart_read {} {
set val [jtag_get]
while {($val & 1) == 1} {
jtag_val 2 0
set val [jtag_get]
set inb [expr {$val >> 3}]
puts -nonewline "[format %02X $inb] "
}
puts "."
}
proc jtag_uart_console {} {
jtag_lock
while {1} {
set val [jtag_get]
while {($val & 1) == 1} {
jtag_val 2 0
set val [jtag_get]
set inb [expr {$val >> 3}]
puts -nonewline "[format %c $inb]"
}
}
jtag_unlock
}
proc jtag_write_csr {csr val} {
jtag_cmd 0 5
jtag_val 0 "[expr {($val >> 24) & 0xff}]"
jtag_val 0 "[expr {($val >> 16) & 0xff}]"
jtag_val 0 "[expr {($val >> 8) & 0xff}]"
jtag_val 0 "[expr {($val >> 0) & 0xff}]"
jtag_val 0 "$csr"
return [jtag_low 2]
}
proc jtag_break {} {
jtag_cmd 0 6
}
proc jtag_reset {} {
jtag_cmd 0 7
}
# Move back to idle state
proc jtag_sync {} {
for {set i 0} {$i < 10} {incr i} {
jtag_cmd 0 0
after 20
}
}
################################# ASM #################################
proc opcode {val} {
switch $val {
0 { return "srui" }
1 { return "nori" }
2 { return "muli" }
3 { return "sh" }
4 { return "lb" }
5 { return "sri" }
6 { return "xori" }
7 { return "lh" }
8 { return "andi" }
9 { return "xnori" }
10 { return "lw" }
11 { return "lhu" }
12 { return "sb" }
13 { return "addi" }
14 { return "ori" }
15 { return "sli" }
16 { return "lbu" }
17 { return "be" }
18 { return "bg" }
19 { return "bge" }
20 { return "bgeu" }
21 { return "bgu" }
22 { return "sw" }
23 { return "bne" }
24 { return "andhi" }
25 { return "cmpei" }
26 { return "cmpgi" }
27 { return "cmpgei" }
28 { return "cmpgeui" }
29 { return "cmpgui" }
30 { return "orhi" }
31 { return "cmpnei" }
32 { return "sru" }
33 { return "nor" }
34 { return "mul" }
35 { return "divu" }
36 { return "rcsr" }
37 { return "sr" }
38 { return "xor" }
39 { return "div" }
40 { return "and" }
41 { return "xnor" }
42 { return "??" }
43 { return "raise" }
44 { return "sextb" }
45 { return "add" }
46 { return "or" }
47 { return "sl" }
48 { return "b" }
49 { return "modu" }
50 { return "sub" }
51 { return "??" }
52 { return "wcsr" }
53 { return "mod" }
54 { return "call" }
55 { return "sexth" }
56 { return "bi" }
57 { return "cmpe" }
58 { return "cmpg" }
59 { return "cmpge" }
60 { return "cmpgeu" }
61 { return "cmpgu" }
62 { return "calli" }
63 { return "cmpne" }
}
}
proc reg {i} {
switch $i {
26 { return "gp" }
27 { return "fp" }
28 { return "sp" }
29 { return "ra" }
30 { return "ea" }
31 { return "ba" }
default { return "r$i" }
}
}
proc csr_reg {i} {
switch $i {
0 { return "IE" }
1 { return "IM" }
2 { return "IP" }
3 { return "ICC" }
4 { return "DCC" }
5 { return "CC" }
6 { return "CFG" }
7 { return "EBA" }
8 { return "DC" }
9 { return "DEBA" }
14 { return "JTX" }
15 { return "JRX" }
16 { return "BP0" }
17 { return "BP1" }
18 { return "BP2" }
19 { return "BP3" }
24 { return "WP0" }
25 { return "WP1" }
26 { return "WP2" }
27 { return "WP3" }
}
}
proc imm16 {i} {
if {$i >= 32768} {
return "-[expr {65536 - $i}]"
} else {
return "+$i"
}
}
proc imm26 {i} {
if {$i >= 33554432} {
return "-[expr {67108864 - $i}]"
} else {
return "+$i"
}
}
proc opfmt {op} {
set code [expr {$op >> 26}]
set r0 [expr {($op >> 21) & 31}]
set r1 [expr {($op >> 16) & 31}]
set r2 [expr {($op >> 11) & 31}]
set i16 [expr {$op & 0xffff}]
set i26 [expr {$op & 0x3ffffff}]
if {$code == 4 || $code == 7 || $code == 10 || $code == 11 || $code == 16} {
# lb, lh, lw, lhu, lbu
return "[opcode $code] [reg $r1], ([reg $r0][imm16 $i16])"
} elseif {$code == 3 || $code == 12 || $code == 22} { # sh, sb, sw
return "[opcode $code] ([reg $r0][imm16 $i16]), [reg $r1]"
} elseif {$code <= 32} { # (op, op, imm) instruction
return "[opcode $code] [reg $r1], [reg $r0], [imm16 $i16]"
} elseif {$code == 48 || $code == 54} { # b, call
return "[opcode $code] [reg $r0]"
} elseif {$code == 36} { # rcsr
return "[opcode $code] [reg $r2], [csr_reg $r0]"
} elseif {$code == 52} { # wcsr
return "[opcode $code] [csr_reg $r0], [reg $r1]"
} elseif {$code == 56 || $code == 62 || $code == 43} { # bi, calli, raise
return "[opcode $code] [imm26 $i26]"
} elseif {$code == 44 || $code == 55} { # sextb, sexth
return "[opcode $code] [reg $r2], [reg $r0]"
} else {
return "[opcode $code] [reg $r2], [reg $r1], [reg $r0]"
}
}
################################ CMDS #################################
proc reset {} {
jtag_lock
jtag_reset
jtag_unlock
}
proc cpu_break {} {
jtag_lock
jtag_break
jtag_unlock
}
proc sync {} {
jtag_lock
jtag_sync
jtag_unlock
}
proc read_memory {addr len} {
jtag_lock
if {$addr == ""} {set addr 0}
if {$len == ""} {set len 64}
# Align read to 16-byte boundary
set a_addr [expr {$addr & ~0xf}]
set a_len [expr {($len + 15) & ~0xf}]
set vals [jtag_read_memory $a_addr $a_len]
for {set i 0} {$i < $a_len} {set i [expr {$i + 16}]} {
puts -nonewline [format %08X: $a_addr]
set a_addr [expr {$a_addr + 16}]
for {set j 0} {$j < 4} {incr j} {
set vals [lassign $vals b0 b1 b2 b3]
puts -nonewline " $b0$b1$b2$b3"
}
puts ""
}
set nextcmd [list]
lappend nextcmd "read"
lappend nextcmd $a_addr
lappend nextcmd $a_len
jtag_unlock
return $nextcmd
}
proc write_memory {addr val} {
jtag_lock
set data [list]
lappend data [expr {($val >> 24) & 0xff}]
lappend data [expr {($val >> 16) & 0xff}]
lappend data [expr {($val >> 8) & 0xff}]
lappend data [expr {($val >> 0) & 0xff}]
jtag_write_memory $addr $data
jtag_unlock
}
proc dump_memory {addr len} {
jtag_lock
if {$addr == ""} {set addr 0}
if {$len == ""} {set len 16}
# Align read to 4-byte boundary
set a_addr [expr {$addr & ~0x3}]
set a_len [expr {$len * 4}]
set a_end [expr {$a_addr + $a_len}]
set vals [jtag_read_memory $a_addr $a_len]
for {set a $a_addr} {$a < $a_end} {set a [expr {$a + 4}]} {
set vals [lassign $vals b0 b1 b2 b3]
puts "[format %08X $a]: [opfmt 0x$b0$b1$b2$b3]"
}
set nextcmd [list]
lappend nextcmd "dump"
lappend nextcmd [expr {$a_addr + $a_len}]
lappend nextcmd [expr {$a_len / 4}]
jtag_unlock
return $nextcmd
}
proc csr {csr val} {
jtag_lock
jtag_write_csr $csr $val
jtag_unlock
}
proc recv {} {
jtag_lock
jtag_uart_read
jtag_unlock
}
proc send {data} {
jtag_lock
foreach j $data {
jtag_uart_write $j
}
jtag_unlock
}
proc transfer {prompt file offset len target} {
set data [open $file]
fconfigure $data -translation binary
seek $data $offset
set progress 0
for {set done 0} {$done < $len} {set done [expr {$done+$did}]} {
puts -nonewline "\r$prompt$done bytes"
set rest [expr {$len - $done}]
if {$rest > 100} { set do 100 } else { set do $rest }
set bytes [read $data $do]
set chunk [list]
for {set i 0} {$i < $do} {incr i} {
scan [string index $bytes $i] %c v
lappend chunk $v
}
#puts "$chunk = [llength $chunk]"
set did [llength $chunk]
if {$did != $do} {
puts "\n -- Short transfer error!"
break
}
jtag_write_memory $target $chunk
set target [expr {$target+$did}]
}
if {$done == $len} {
puts "\r$prompt[format %d $len] bytes - complete"
}
close $data
}
proc cmpBytefield { ilist1 ilist2 target offset} {
set len1 [llength $ilist1]
set len2 [llength $ilist2]
#if list lengths differ, return length of the shorter list, else compare
#puts "file $len1 ram $len2"
if {$len1 < $len2} {
puts "Chunk bigger than Vals!"
return $len1
} elseif {$len2 < $len1} {
puts "Vals bigger than Chunk!"
return $len2
} else {
#compare elements. return -1 if bytefields are equal,
#else return the offset of first diff
for { set i 0 } { $i < $len1 } { incr i } {
set elem1 [format %2.2X [lindex $ilist1 $i]]
set elem2 [lindex $ilist2 $i]
if {$elem1 != $elem2} {
puts ""
puts ""
puts "Verify failed, diff follows:"
puts ""
set a_addr [expr {$target & ~0xf}]
puts "RAM Content:"
puts "*********************************************"
for {set k 0} {$k < $len1} {set k [expr {$k + 16}]} {
puts ""
puts -nonewline [format %08X: $a_addr]
set a_addr [expr {$a_addr + 16}]
for {set j 0} {$j < 4} {incr j} {
set ilist2 [lassign $ilist2 b0 b1 b2 b3]
if {[expr {$i/4}] == [expr {[expr {$k/4}] + $j}] } {
puts -nonewline " \033\[31m$b0$b1$b2$b3\033\[0m"
} else {
puts -nonewline " $b0$b1$b2$b3"
}
}
}
puts ""
set a_addr [expr {[expr {$target + $offset}] & ~0xf}]
puts ""
puts "ELF Content:"
puts "*********************************************"
for {set k 0} {$k < $len1} {set k [expr {$k + 16}]} {
puts ""
puts -nonewline [format %08X: $a_addr]
set a_addr [expr {$a_addr + 16}]
for {set j 0} {$j < 4} {incr j} {
set ilist1 [lassign $ilist1 b0 b1 b2 b3]
if {[expr {$i/4}] == [expr {[expr {$k/4}] + $j}] } {
puts -nonewline " \033\[31m[format %02X $b0][format %02X $b1][format %02X $b2][format %02X $b3]\033\[0m"
} else {
puts -nonewline " [format %02X $b0][format %02X $b1][format %02X $b2][format %02X $b3]"
}
}
}
puts ""
puts ""
return $i
}
}
return -1
}
}
proc compare {section file offset len target} {
set prompt " section $section: "
set data [open $file]
fconfigure $data -translation binary
seek $data $offset
set progress 0
#puts "Working $section"
for {set done 0} {$done < $len} {set done [expr {$done+$did}]} {
set rest [expr {$len - $done}]
if {$rest > 128} { set do 128 } else { set do $rest }
set bytes [read $data $do]
set chunk [list]
for {set i 0} {$i < $do} {incr i} {
scan [string index $bytes $i] %c v
lappend chunk $v
}
#puts "$chunk = [llength $chunk]"
set did [llength $chunk]
if {$did != $do} {
puts "\n -- Short transfer error!"
break
}
# Align read to 4-byte boundary
set a_addr $target
set a_len $did
set a_end [expr {$a_addr + $a_len}]
#puts "read vals... START $a_addr LEN $a_len END $a_end"
set vals [jtag_read_memory $a_addr $a_len]
#puts "read done."
#modify file content of boot section to match CPU trap in ram
# if {[string equal $section ".boot"]==1 && $done == 0 } {
# set chunk [lreplace $chunk 0 3 224 0 0 0]
# puts "Replaced file content with CPU trap"
# }
set cmpresult [cmpBytefield $chunk $vals $target $offset]
if {$cmpresult != -1} {
puts ""
puts -nonewline "Verify failed at offset "
puts "[format %#8.8X [expr {$cmpresult+$target}]]"
puts ""
break
} else {
puts -nonewline "\r$prompt$done bytes"
}
set target [expr {$target+$did}]
}
if {$done == $len} {
puts "\r$prompt[format %d $len] bytes - Verify complete"
}
close $data
return $cmpresult
}
proc verify {file} {
if {$file == ""} {
puts "Specify file to load!"
return
}
jtag_lock
#puts -nonewline "Capturing the CPU at address 0x0: "
# for {set i 0} {$i < 20} {incr i} {
# # bi +0 (CPU trap)
# jtag_write_memory 0x0 { 0xE0 0x00 0x00 0x00 }
# # flush instruction cache
# jtag_write_csr 0x3 0x0
# # Position CPU on this trap
# jtag_reset
# }
# # Wait a bit to be sure the CPU has the trap good and cached
# after 20
# puts "done"
set sections [list]
set sf [open "| readelf -S $file" "r"]
while {[gets $sf line] >= 0} {
if {[regexp {^\s+\[..\]\s+(\.\w+)\s+[^0-9a-f]*\s[0-9a-f]{4,}\s+([0-9a-f]{4,})\s+([0-9a-f]{4,})\s} $line nil section offset size] == 0} continue
lappend sections "$section 0x$offset 0x$size"
}
close $sf
set lf [open "| readelf -l $file" "r"]
while {[gets $lf line] >= 0} {
if {[regexp {^\s*LOAD\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+} $line nil offset vaddr paddr len] == 0} continue
puts "Verifying $offset+$len to $paddr"
if {$paddr != $vaddr} {
puts " Physical and virtual address mismatch! - Skipping"
continue
}
foreach j $sections {
lassign [split $j " "] section off size
set taddr [expr {$paddr+$off-$offset}]
if {$offset <= $off && $off+$size <= $offset+$len} {
if {[compare $section $file $off $size $taddr] != -1} {
break
}
} elseif {$offset <= $off && $off < $offset+$len} {
puts " section $section: only half contained??"
}
}
}
close $lf
# puts -nonewline "Releasing CPU: "
# jtag_write_csr 0x4 0x0
# after 20
# jtag_write_csr 0x3 0x0
# puts done
jtag_unlock
# jtag_reset
# jtag_unlock
}
proc load {file} {
if {$file == ""} {
puts "Specify file to load!"
return
}
jtag_lock
puts -nonewline "Capturing the CPU at address 0x0: "
for {set i 0} {$i < 20} {incr i} {
# bi +0 (CPU trap)
jtag_write_memory 0x0 { 0xE0 0x00 0x00 0x00 }
# flush instruction cache
jtag_write_csr 0x3 0x0
# Position CPU on this trap
jtag_reset
}
# Wait a bit to be sure the CPU has the trap good and cached
after 20
puts "done"
set sections [list]
set sf [open "| readelf -S $file" "r"]
while {[gets $sf line] >= 0} {
if {[regexp {^\s+\[..\]\s+(\.\w+)\s+[^0-9a-f]*\s[0-9a-f]{4,}\s+([0-9a-f]{4,})\s+([0-9a-f]{4,})\s} $line nil section offset size] == 0} continue
lappend sections "$section 0x$offset 0x$size"
}
close $sf
# We can safely overwrite all of the instruction bus (even 0x0) now.
# The trap is certainly cached and the CPU will not see the new values.
set lf [open "| readelf -l $file" "r"]
while {[gets $lf line] >= 0} {
if {[regexp {^\s*LOAD\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+} $line nil offset vaddr paddr len] == 0} continue
puts "Loading $offset+$len to $paddr"
if {$paddr != $vaddr} {
puts " Physical and virtual address mismatch! - Skipping"
continue
}
foreach j $sections {
lassign [split $j " "] section off size
set taddr [expr {$paddr+$off-$offset}]
if {$offset <= $off && $off+$size <= $offset+$len} {
transfer " section $section: " $file $off $size $taddr
} elseif {$offset <= $off && $off < $offset+$len} {
puts " section $section: only half contained??"
}
}
}
close $lf
# The CPU is spinning at address 0, so no need to reset it.
# First flush the dcache and then release the CPU by flushing the icache
puts -nonewline "Releasing CPU: "
jtag_write_csr 0x4 0x0
after 20
jtag_write_csr 0x3 0x0
puts done
jtag_unlock
}
# Reverse a hex string
proc reverseHex {hexstring} {
set result ""
for {set x 0} {$x < [string length $hexstring]} {incr x} {
set piece [string range $hexstring $x [expr {$x+1}]]
set result "${piece}${result}"
incr x
}
return $result
}
# A pure TCL implementation of very basic JSON parsing.
# Xilinx's TCL is too old to support the standard json library right now.
proc dirty_json {json field} {
set indx [string first "\"$field\"" $json]
if {$indx == -1} {
return ""
}
set indx [expr {$indx + [string length $field] + 2}]
set json [string range $json $indx end]
set indx [string first "\"" $json]
if {$indx == -1} {
return ""
}
set indx [expr {$indx + 1}]
set json [string range $json $indx end]
set endx [string first "\"" $json]
if {$indx == -1} {
return ""
}
set endx [expr {$endx - 1}]
return [string range $json 0 $endx]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment