The changes in this patch:
1.  add a JTAG capture pin
=> allows removing sensitivity to reg_update which caused clocking problems making JTAG unstable
2. support register file backed by ram blocks
  => saves quite some area and speed on altera
  ... be sure to enable it using `define CFG_EBR_POSEDGE_REGISTER_FILE
3. Fix a minor problem where compilation fails when interrupts are not supported
4. Add support to flush icache and dcache per JTAG
5. Fix wrong width assignments for PC
6. Marginally pipeline the multiplier
  => not a lot, but enough to move it off the critical path

I've also attached my firmware loading tool. It needs quartus to work with Altera FPGAs, though.

Attachment: gsi-lm32-tweaks.tar.gz
Description: GNU Zip compressed data

#! /bin/bash
# \
export RLWRAP_ #\
exec rlwrap -C lm32-ctl -I /opt/quartus/quartus/bin/quartus_stp --64bit -t "$0" 
"$@"

########################### LOW LEVEL ACCESS #########################
proc jtag_put {val} {
  device_virtual_dr_shift -instance_index 0 -length 11 -dr_value "[format %03X 
$val]" -value_in_hex -no_captured_dr_value
}

proc jtag_get {} {
  return 0x[device_virtual_dr_shift -instance_index 0 -length 11 -dr_value 000 
-value_in_hex]
}

############################ SAFE-ISHL ACCESS #########################

proc jtag_val {idx val} {
  set v [expr {($val << 3) | $idx}]
  jtag_put "$v"
}

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]
  
  if {$len > 0} {
    lappend out "[format %02X [jtag_read_addr $addr]]"
  }
  
  for {set i 1} {$i < $len} {incr i} {
    #set x [expr {$addr+$i}]
    #lappend out "[format %02X [jtag_read_addr $x]]"
    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} {
      set first 0
      jtag_write_addr "$addr" "$j"
    } else {
      jtag_write_next "$j"
    }
  }
}

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_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 {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 $r0]"
  } elseif {$code == 52} { # wcsr
    return "[opcode $code] [csr $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 read_memory {addr len} {
  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
}

proc write_memory {addr val} {
  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
}

proc dump_memory {addr len} {
  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}]
}

proc send {data} {
  foreach j $data {
    jtag_uart_write $j
  }
}

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 load {file} {
  if {$file == ""} {
    puts "Specify file to load!"
    return
  }
  
  puts -nonewline "Capturing the CPU at address 0x0: "
  for {set i 0} {$i < 20} {incr i} {
    # bi +0 (CPU trap)
    write_memory 0x0 0xE0000000
    # 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
      if {$offset <= $off && $off+$size <= $offset+$len} {
        transfer " section $section: " $file $off $size [expr 
{$paddr+$off-$offset}]
      } 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
}

################################ MAIN #################################


# 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
        }
}
puts "\nSelect JTAG chain connected to $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 "\nSelect device: $test_device.\n";

# Open device 
open_device -hardware_name $usbblaster_name -device_name $test_device

device_lock -timeout 10000
device_virtual_ir_shift -instance_index 0 -ir_value 1 -no_captured_ir_value

jtag_sync
while {$cmd != "quit"} {
  puts -nonewline "\nlm32> "

  if {[gets stdin line] < 0} break
  
  if {$line eq ""} { set line $nextcmd }
  
  set parts [split $line " "]
  set args [lassign $parts cmd]
  set tail [lassign $args arg1 arg2 arg3 arg4]
  
  set nextcmd ""
  switch $cmd {
    "" { }
    "break" { jtag_break }
    "reset" { jtag_reset }
    "sync" { jtag_sync }
    "read" { set nextcmd [read_memory $arg1 $arg2] }
    "dump" { set nextcmd [dump_memory $arg1 $arg2] }
    "write" { write_memory $arg1 $arg2 }
    "csr" { jtag_write_csr $arg1 $arg2 }
    "recv" { jtag_uart_read }
    "send" { send $args }
    "load" { load $arg1 }
    "quit" { }
    default { puts "Unknown command" }
  }
}

# Close device
device_unlock
puts "Bye!"
close_device
_______________________________________________
http://lists.milkymist.org/listinfo.cgi/devel-milkymist.org
IRC: #milkymist@Freenode
Twitter: www.twitter.com/milkymistvj
Ideas? http://milkymist.uservoice.com

Reply via email to