summaryrefslogtreecommitdiffstats
path: root/de1/fpga-flash-nor/de1flash.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'de1/fpga-flash-nor/de1flash.tcl')
-rw-r--r--de1/fpga-flash-nor/de1flash.tcl423
1 files changed, 423 insertions, 0 deletions
diff --git a/de1/fpga-flash-nor/de1flash.tcl b/de1/fpga-flash-nor/de1flash.tcl
new file mode 100644
index 0000000..5556e3b
--- /dev/null
+++ b/de1/fpga-flash-nor/de1flash.tcl
@@ -0,0 +1,423 @@
+## -----------------------------------------------------------------------
+##
+## Copyright 2011 H. Peter Anvin - All Rights Reserved
+##
+## Permission is hereby granted, free of charge, to any person
+## obtaining a copy of this software and associated documentation
+## files (the "Software"), to deal in the Software without
+## restriction, including without limitation the rights to use,
+## copy, modify, merge, publish, distribute, sublicense, and/or
+## sell copies of the Software, and to permit persons to whom
+## the Software is furnished to do so, subject to the following
+## conditions:
+##
+## The above copyright notice and this permission notice shall
+## be included in all copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+## OTHER DEALINGS IN THE SOFTWARE.
+##
+## -----------------------------------------------------------------------
+
+# Virtual JTAG instance index
+set fl_instance 0
+# Current IR value
+set fl_current_ir -1
+# Known space in the write FIFO
+set fl_free_space 0
+
+# Device parameters which really should come from reading the CFI data
+set fl_device_size 0x400000
+proc fl_sector_size addr {
+ expr ($addr < 65536) ? 8192 : 65536
+}
+
+proc select_ir new_ir {
+ global fl_instance fl_current_ir
+ if { $new_ir != $fl_current_ir } {
+ device_virtual_ir_shift -instance_index $fl_instance \
+ -ir_value $new_ir -no_captured_ir_value
+ set fl_current_ir $new_ir
+ }
+}
+
+# Send a command (given as a 72-bit hex string)
+proc fl_send cmd {
+ global fl_instance fl_free_space
+
+ while { $fl_free_space < 1 } {
+ # Poll the free space register until nonzero
+ select_ir 2
+ scan [device_virtual_dr_shift -instance_index $fl_instance \
+ -length 8 -value_in_hex] "%x" fl_free_space
+ }
+
+ select_ir 3
+ device_virtual_dr_shift -instance_index $fl_instance \
+ -dr_value $cmd -length 72 -value_in_hex -no_captured_dr_value
+ incr fl_free_space -1
+}
+
+# Receive a reply (returned as a 72-bit hex string)
+proc fl_recv {} {
+ global fl_instance
+
+ set status 0
+
+ select_ir 1
+ while { ($status & 0x80) == 0 } {
+ set v [device_virtual_dr_shift -instance_index $fl_instance \
+ -length 72 -value_in_hex]
+ scan $v "%2x" status
+ }
+ return $v
+}
+
+# Receive bulk data (returned as a binary string)
+proc fl_recv_bulk bytes {
+ global fl_instance
+
+ select_ir 1
+
+ set qwords [expr ($bytes + 7) >> 3]
+ set outdata ""
+
+ while { $qwords } {
+ set d [device_virtual_dr_shift -instance_index $fl_instance \
+ -length [expr $qwords * 72] -value_in_hex]
+
+ # The LSW is the first transaction, so we need to process
+ # the returned string backwards
+ for { set n [expr $qwords - 1] } { $n >= 0 } { incr n -1 } {
+ set ix [expr $n * 18]
+ set di [string range $d $ix [expr $ix + 17]]
+ scan $di "%2x%2x%2x%2x%2x%2x%2x%2x%2x" st d7 d6 d5 d4 d3 d2 d1 d0
+ if { $st & 0x80 } {
+ set bd [binary format cccccccc $d0 $d1 $d2 $d3 $d4 $d5 $d6 $d7]
+ set nbytes 8
+ if { $bytes < 8 } {
+ set bd [string range $bd 0 [expr $bytes - 1]]
+ set nbytes $bytes
+ }
+ append outdata $bd
+ incr qwords -1
+ set bytes [expr $bytes - $nbytes]
+ } else {
+ # puts "Dropping non-data: $di"
+ }
+ }
+ }
+ return $outdata
+}
+
+# Reset state machine
+proc fl_reset {} {
+ global fl_instance
+
+ puts -nonewline "Issuing reset... "
+
+ select_ir 5
+ device_virtual_dr_shift -instance_index $fl_instance \
+ -dr_value 1 -length 1 -no_captured_dr_value
+
+ select_ir 4
+ set rdy 0
+ while { !$rdy } {
+ set rdy [device_virtual_dr_shift -instance_index $fl_instance -length 1]
+ }
+
+ puts "done."
+}
+
+# Sector erase
+proc fl_erase addr {
+ set secsize [fl_sector_size $addr]
+ set secaddr [expr $addr & ~($secsize - 1)]
+
+ puts -nonewline [format "Erasing sector at 0x%x(0x%x)... " $secaddr $secsize]
+
+ # Blank check the sector before erasing
+ fl_send [format "50%08X%08X" $secaddr $secsize]
+ set zc [fl_recv]
+ set ze [format "\[89ABCDEF\]5%08XFFFFFFFF" [expr $secaddr + $secsize]]
+
+ if { ![string match $ze $zc] } {
+ # puts "\nNot blank, expected $ze got $zc"
+
+ fl_send 81AAAAAAAA000000AA
+ fl_send 815555555500000055
+ fl_send 81AAAAAAAA00000080
+ fl_send 81AAAAAAAA000000AA
+ fl_send 815555555500000055
+ fl_send [format "81%08X00000030" $secaddr]
+
+ set read_cmd [format "D0%08X%08X" $secaddr 8]
+
+ set done 0
+ while { !$done } {
+ fl_send $read_cmd
+ set v [fl_recv]
+ set done [string match {[89ABCDEF]DFFFFFFFFFFFFFFFF} $v]
+ }
+ puts "done."
+ } else {
+ puts "already blank."
+ }
+}
+
+# Read file
+proc fl_read_file { file addr size } {
+ set f [open $file {WRONLY CREAT TRUNC BINARY}]
+
+ puts -nonewline [format "Reading file %s@0x%x,0x%x... " $file $addr $size]
+
+ # This is an arbitrary tunable, but at least for Quartus 11 is doesn't
+ # seem to get any better with a larger block size...
+ set max_block 4096
+
+ # Read data
+ set left $size
+ fl_send [format "D0%08X%08X" $addr $size]
+ while { $left > 0 } {
+ set blk [expr ($left < $max_block) ? $left : $max_block]
+ puts -nonewline $f [fl_recv_bulk $blk]
+ set left [expr $left - $blk]
+ }
+
+ close $f
+ puts "done."
+}
+
+# Write file (without erase)
+proc fl_write_file { file addr size } {
+ set f [open $file {RDONLY BINARY}]
+
+ puts -nonewline [format "Writing file %s@0x%x,0x%x... " $file $addr $size]
+
+ # Write data
+ set left $size
+ fl_send [format "D0%08X%08X" $addr 0]
+ while { $left > 0 } {
+ set blk [expr ($left < 8) ? $left : 8]
+ set d [read $f $blk]
+ append d [string repeat "\xff" [expr 8 - $blk]]
+ binary scan $d H2H2H2H2H2H2H2H2 d0 d1 d2 d3 d4 d5 d6 d7
+ fl_send [format "B%X%s%s%s%s%s%s%s%s" $blk $d7 $d6 $d5 $d4 $d3 $d2 $d1 $d0]
+ set left [expr $left - $blk]
+ }
+
+ # Synchronize
+ puts -nonewline "syncing... "
+ fl_send 6000000000AEAEAEAE
+ set v [fl_recv]
+ if { [string match $v [format "\[89ABCDEF\]6%08XAEAEAEAE" [expr $addr + $size]]] } {
+ puts "error!"
+ } else {
+ puts "done."
+ }
+
+ close $f
+}
+
+# Checksum (CRC32) a region
+proc fl_crc32_file { file addr size } {
+ fl_send [format "70%08X%08X" $addr $size]
+ set v [fl_recv]
+ scan $v "%1x%1x%8x%8x" ctr type end crc
+ if { $type != 7 || $end != $addr + $size } {
+ puts "$file: checksumming failure"
+ } else {
+ puts [format "%08x %s@0x%x,0x%x" $crc $file $addr $size]
+ }
+}
+
+# Parse a list of filespecs
+proc fl_parse_files { mode files } {
+ global fl_device_size
+
+ set last_address 0
+ set file_list {}
+
+ foreach file $files {
+ regexp {^(.*?)(\@([0-9a-fA-FxX]+|)(,([0-9a-fA-FxX]+)|)|)$} $file \
+ junk0 filename junk1 address junk2 len
+ if { [string equal $address ""] } {
+ set address $last_address
+ }
+ if { [string equal $len ""] } {
+ switch $mode {
+ read
+ {
+ set len [expr $fl_device_size - $address]
+ }
+ write
+ {
+ if { [string equal $filename ""] } {
+ set len [expr $fl_device_size - $address]
+ } else {
+ set len [file size $filename]
+ }
+ }
+ }
+ }
+ lappend file_list [list $filename $address $len]
+ }
+
+ return $file_list
+}
+
+# Produce a list of erase blocks from a filespec list
+proc fl_get_eraseblocks flist {
+ set erase_list {}
+
+ foreach fspec $flist {
+ set addr [lindex $fspec 1]
+ set len [lindex $fspec 2]
+
+ set end [expr $addr + $len]
+
+ while { $addr < $end } {
+ set secsize [fl_sector_size $addr]
+ set secaddr [expr $addr & ~($secsize - 1)]
+
+ lappend erase_list $secaddr
+ set addr [expr $secaddr + $secsize]
+ }
+ }
+
+ return [lsort -integer -unique $erase_list]
+}
+
+# Erase per filespec
+proc fl_erase_files flist {
+ foreach eb [fl_get_eraseblocks $flist] {
+ fl_erase $eb
+ }
+}
+
+# Read per filespec
+proc fl_read_files flist {
+ foreach fspec $flist {
+ set file [lindex $fspec 0]
+ set addr [lindex $fspec 1]
+ set len [lindex $fspec 2]
+
+ if { ![string equal $file ""] } {
+ fl_read_file $file $addr $len
+ }
+ }
+}
+
+# Write per filespec
+proc fl_write_files flist {
+ foreach fspec $flist {
+ set file [lindex $fspec 0]
+ set addr [lindex $fspec 1]
+ set len [lindex $fspec 2]
+
+ if { ![string equal $file ""] } {
+ fl_write_file $file $addr $len
+ }
+ }
+}
+
+# Checksum per filespec
+proc fl_crc32_files flist {
+ foreach fspec $flist {
+ set file [lindex $fspec 0]
+ set addr [lindex $fspec 1]
+ set len [lindex $fspec 2]
+
+ fl_crc32_file $file $addr $len
+ }
+}
+
+# ---------------------------------------------------------------------------
+# Start of main program
+# ---------------------------------------------------------------------------
+
+if {$argc < 1} {
+ error "Usage: quartus_stp -t de1flash.tcl command [filespec...]"
+}
+
+set fl_cmd [lindex $argv 0]
+set fl_fspec [lrange $argv 1 end]
+
+# List all available programming hardwares, and select the USBBlaster.
+# (Note: this example assumes only one USBBlaster connected.)
+#puts "Programming Hardwares:"
+#foreach hardware_name [get_hardware_names] {
+# puts $hardware_name
+# if { [string match "USB-Blaster*" $hardware_name] } {
+# set usbblaster_name $hardware_name
+# }
+#}
+
+set hardwares [get_hardware_names]
+set usbblaster_name [lindex $hardwares 0]
+
+puts "\nUsing programming cable \{$usbblaster_name\}.\n";
+
+# List all devices on the chain, and select the first device on the
+# chain.
+puts "\nDevices on the JTAG chain:"
+foreach device_name [get_device_names -hardware_name $usbblaster_name] {
+ puts $device_name
+ if { [string match "@1*" $device_name] } {
+ set test_device $device_name
+ }
+}
+puts "\nSelecting device: \{$test_device\}.\n";
+
+# Open device
+open_device -hardware_name $usbblaster_name -device_name $test_device
+
+device_lock -timeout 120000
+
+fl_reset
+
+switch $fl_cmd {
+ write
+ {
+ set fspec [fl_parse_files write $fl_fspec]
+ fl_erase_files $fspec
+ fl_write_files $fspec
+ }
+ writeonly
+ {
+ set fspec [fl_parse_files write $fl_fspec]
+ fl_write_files $fspec
+ }
+ erase
+ {
+ set fspec [fl_parse_files write $fl_fspec]
+ fl_erase_files $fspec
+ }
+ read
+ {
+ set fspec [fl_parse_files read $fl_fspec]
+ fl_read_files $fspec
+ }
+ crc32
+ {
+ set fspec [fl_parse_files write $fl_fspec]
+ fl_crc32_files $fspec
+ }
+ eraseall
+ {
+ fl_erase_files [list [list "" 0 $fl_device_size]]
+ }
+ default
+ {
+ error "Unknown de1flash command"
+ }
+}
+
+device_unlock
+close_device