140 lines
5.3 KiB
Python
140 lines
5.3 KiB
Python
|
#!/usr/bin/env python3
|
||
|
|
||
|
# Copyright (c) 2020, Bo Gao <7zlaser@gmail.com>
|
||
|
|
||
|
# Redistribution and use in source and binary forms, with or without modification,
|
||
|
# are permitted provided that the following conditions are met:
|
||
|
|
||
|
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||
|
# conditions and the following disclaimer.
|
||
|
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||
|
# of conditions and the following disclaimer in the documentation
|
||
|
# and/or other materials provided with the distribution.
|
||
|
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||
|
# used to endorse or promote products derived from this software without specific
|
||
|
# prior written permission.
|
||
|
|
||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||
|
# SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
|
||
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
import sys, usb, time, binascii
|
||
|
|
||
|
class GowinJTAG:
|
||
|
dev=None
|
||
|
|
||
|
# Open USB device
|
||
|
def __init__(self):
|
||
|
self.dev=usb.core.find(idVendor=0x20a0, idProduct=0x4209)
|
||
|
if self.dev is None:
|
||
|
print("Error: device not found")
|
||
|
sys.exit(-1)
|
||
|
self.dev.reset()
|
||
|
self.dev.set_configuration()
|
||
|
|
||
|
# Close USB device
|
||
|
def __del__(self):
|
||
|
try:
|
||
|
usb.util.dispose_resources(self.dev)
|
||
|
except Exception: pass
|
||
|
|
||
|
# Write JTAG command
|
||
|
def WriteCmd(self, cmd):
|
||
|
self.dev.write(0x01, [0x10, 0x30, 0x80, 0x01, 0x00, cmd&0xff, 0x00])
|
||
|
|
||
|
# Reset FPGA
|
||
|
def ResetPwr(self):
|
||
|
self.dev.write(0x01, [0x00])
|
||
|
time.sleep(0.01)
|
||
|
self.dev.write(0x01, [0x01])
|
||
|
|
||
|
# Reset JTAG
|
||
|
def ResetTap(self):
|
||
|
self.dev.write(0x01, [0x10, 0xff, 0x00, 0x00, 0x00])
|
||
|
|
||
|
# Reset SRAM
|
||
|
def ResetRam(self):
|
||
|
self.WriteCmd(0x15) # Enter config mode
|
||
|
self.WriteCmd(0x05) # Clear SRAM
|
||
|
self.WriteCmd(0x02) # Nop
|
||
|
time.sleep(0.01) # Wait for finish
|
||
|
self.WriteCmd(0x3a) # Exit config mode
|
||
|
self.WriteCmd(0x02) # Nop
|
||
|
|
||
|
# Read JTAG ID
|
||
|
def ReadId(self):
|
||
|
self.WriteCmd(0x11) # Read JTAG ID command
|
||
|
self.dev.write(0x01, [0x11, 0x20, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
|
||
|
ret=self.dev.read(0x81, 64, 100)
|
||
|
ret.reverse()
|
||
|
return bytes(ret[1:5]).hex()
|
||
|
|
||
|
# Write SRAM
|
||
|
def WriteRam(self, filename):
|
||
|
flen=0
|
||
|
self.WriteCmd(0x15) # Enter config mode
|
||
|
self.WriteCmd(0x17) # Write SRAM
|
||
|
self.dev.write(0x01, [0x10, 0x20, 0x00]) # Shift DR
|
||
|
with open(filename, "rb") as f:
|
||
|
while True:
|
||
|
dat=f.read(63)
|
||
|
if not dat:
|
||
|
break
|
||
|
# SPI mode, MSB first
|
||
|
self.dev.write(0x01, [0x20]+list(dat))
|
||
|
flen=f.tell()
|
||
|
self.dev.write(0x01, [0x10, 0x03, 0x00]) # Idle
|
||
|
self.WriteCmd(0x3a) # Exit config mode
|
||
|
self.WriteCmd(0x02) # Nop
|
||
|
return flen
|
||
|
|
||
|
# Enter ISP mode
|
||
|
def EnterIsp(self):
|
||
|
try: # Connection WILL break
|
||
|
self.dev.write(0x01, [0xff])
|
||
|
except Exception: pass
|
||
|
|
||
|
# Entry point
|
||
|
if __name__ == "__main__":
|
||
|
import argparse
|
||
|
import os
|
||
|
parser=argparse.ArgumentParser(description="USB JTAG programmer for Gowin FPGAs")
|
||
|
parser.add_argument("-r", action="store_true", default=False, dest="reset_fpga", help="Reset FPGA")
|
||
|
parser.add_argument("-c", action="store_true", default=False, dest="clear_sram", help="Clear SRAM content")
|
||
|
parser.add_argument("-s", action="store_true", default=False, dest="prog_sram", help="Program SRAM")
|
||
|
parser.add_argument("-f", action="store", dest="prog_filename", help="Specify SRAM filename")
|
||
|
parser.add_argument("-i", action="store_true", default=False, dest="isp_mode", help="Enter adapter ISP mode")
|
||
|
parser.add_argument("-v", action="store_true", default=False, dest="verbose_mode", help="Verbose output")
|
||
|
args=parser.parse_args()
|
||
|
jtag=GowinJTAG()
|
||
|
if args.isp_mode:
|
||
|
jtag.EnterIsp()
|
||
|
time.sleep(0.5)
|
||
|
sys.exit(0)
|
||
|
if args.reset_fpga:
|
||
|
jtag.ResetPwr()
|
||
|
jtag.ResetTap()
|
||
|
if args.verbose_mode:
|
||
|
print("Device ID is 0x"+jtag.ReadId())
|
||
|
if args.clear_sram or args.prog_sram:
|
||
|
jtag.ResetRam()
|
||
|
if args.prog_sram:
|
||
|
if args.prog_filename is None:
|
||
|
print("Error: filename not specified")
|
||
|
sys.exit(-1)
|
||
|
if not os.path.exists(args.prog_filename):
|
||
|
print("Error: cannot open file")
|
||
|
sys.exit(-1)
|
||
|
tbegin=time.time()
|
||
|
flen=jtag.WriteRam(args.prog_filename)
|
||
|
tend=time.time()
|
||
|
if args.verbose_mode:
|
||
|
print("Programming done in {0:.2f}s at {1:.2f}kB/s.".format(tend-tbegin, flen/(tend-tbegin)/1000.0))
|
||
|
sys.exit(0)
|