mirror of
https://github.com/trabucayre/openFPGALoader.git
synced 2026-08-30 01:48:50 +02:00
328 lines
12 KiB
Python
Executable File
328 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import os
|
|
import re
|
|
|
|
from edalize.edatool import get_edatool
|
|
|
|
packages = {
|
|
"Artix": {
|
|
"xc7a12t" : ["cpg238", "csg325"],
|
|
"xc7a15t" : ["cpg236", "csg324", "csg325", "ftg256", "fgg484"],
|
|
"xc7a25t" : ["cpg238", "csg325"],
|
|
"xc7a35t" : ["cpg236", "csg324", "csg325", "ftg256", "fgg484"],
|
|
"xc7a50t" : ["cpg236", "csg324", "csg325", "ftg256", "fgg484"],
|
|
"xc7a75t" : ["csg324", "ftg256", "fgg484", "fgg676"],
|
|
"xc7a100t" : ["csg324", "ftg256", "fgg484", "fgg676"],
|
|
"xc7a200t" : ["sbg484", "fbg484", "fbg676", "ffg1156"],
|
|
},
|
|
# Added but seems not possible to use same bitstream
|
|
# for all Kintex with the same size but different package.
|
|
"Kintex 7": {
|
|
"xc7k70t" : ["fbg484", "fbg676"],
|
|
"xc7k160t" : ["fbg484", "fbg676", "ffg676"],
|
|
"xc7k325t" : ["fbg676", "ffg676", "fbg900", "ffg900"],
|
|
"xc7k355t" : ["ffg901"],
|
|
"xc7k410t" : ["fbg676", "ffg676", "fbg900", "ffg900"],
|
|
"xc7k420t" : ["ffg901", "ffg1156"],
|
|
"xc7k480t" : ["ffg901", "ffg1156"],
|
|
},
|
|
"Spartan 7": {
|
|
"xc7s6" : ["ftgb196", "cpga196", "csga225"],
|
|
"xc7s15" : ["ftgb196", "cpga196", "csga225"],
|
|
"xc7s25" : ["ftgb196", "csga225", "csga324"],
|
|
"xc7s50" : ["ftgb196", "csga324", "fgga484"],
|
|
"xc7s75" : ["fgga484", "fgga676"],
|
|
"xc7s100" : ["fgga484", "fgga676"],
|
|
},
|
|
}
|
|
|
|
if len(os.sys.argv) != 3:
|
|
print("missing board flash type params")
|
|
os.sys.exit()
|
|
part = os.sys.argv[1]
|
|
flash_type = os.sys.argv[2]
|
|
|
|
# Check file type keyword
|
|
assert flash_type in ["spi", "bpi"]
|
|
|
|
build_dir = "tmp_" + part
|
|
if not os.path.isdir(build_dir):
|
|
try:
|
|
os.mkdir(build_dir)
|
|
except OSError:
|
|
print("Creation of the directory %s failed" % build_dir)
|
|
else:
|
|
print("Successfully created the directory %s " % build_dir)
|
|
|
|
currDir = os.path.abspath(os.path.curdir) + "/"
|
|
files = []
|
|
parameters = {}
|
|
pkg_name = None
|
|
pkg = None
|
|
model = ""
|
|
|
|
subpart = part[0:4].lower()
|
|
if subpart == "10cl":
|
|
family = "Cyclone 10 LP"
|
|
tool = "quartus"
|
|
elif subpart == "ep4c":
|
|
family = "Cyclone IV E"
|
|
tool = "quartus"
|
|
elif subpart[0:2] == "5c":
|
|
family = "Cyclone V"
|
|
tool = "quartus"
|
|
files.append({"name": currDir + "constr_cycloneV.tcl",
|
|
"file_type": "tclSource"})
|
|
elif subpart[0:2] == "5s":
|
|
family = "Stratix V"
|
|
tool = "quartus"
|
|
files.append({"name": currDir + "constr_cycloneV.tcl",
|
|
"file_type": "tclSource"})
|
|
elif subpart == "xc7a":
|
|
family = "Artix"
|
|
tool = "vivado"
|
|
model = subpart
|
|
elif subpart == "xc7v":
|
|
family = "Virtex 7"
|
|
tool = "vivado"
|
|
elif subpart == "xc7k":
|
|
device_size = int(part.split("k")[1].split("t")[0])
|
|
if flash_type == "bpi" or device_size <= 160:
|
|
family = "Kintex 7"
|
|
tool = "vivado"
|
|
else:
|
|
family = "Kintex7"
|
|
tool = "ise"
|
|
speed = -2
|
|
model = subpart
|
|
elif subpart == "xc7s":
|
|
family = "Spartan 7"
|
|
tool = "vivado"
|
|
model = subpart
|
|
elif subpart == "xc6s":
|
|
family = "Spartan6"
|
|
tool = "ise"
|
|
speed = -3
|
|
elif subpart == "xc3s":
|
|
family = "Spartan3E"
|
|
tool = "ise"
|
|
speed = -4
|
|
elif subpart == "xc6v":
|
|
family = "Virtex6"
|
|
tool = "ise"
|
|
speed = -1
|
|
elif subpart in ["xcvu", "xcku", "xcau"]:
|
|
family = "Xilinx UltraScale"
|
|
tool = "vivado"
|
|
else:
|
|
print("Error: unknown device")
|
|
os.sys.exit()
|
|
|
|
if model in ["xc7a", "xc7s"]:
|
|
# Accept either a bare device (e.g. "xc7a100t" -> first package) or a
|
|
# device suffixed with a package (e.g. "xc7a100tfgg676" -> "fgg676").
|
|
if part in packages[family]:
|
|
pkg = packages[family][part][0]
|
|
device = part
|
|
else:
|
|
m = re.match(r"(xc7[as]\d+t?)([a-z]+\d+)", part)
|
|
if not m or m.group(1) not in packages[family]:
|
|
print(f"Error: cannot parse part/package from '{part}'")
|
|
os.sys.exit()
|
|
device = m.group(1)
|
|
pkg = m.group(2)
|
|
if pkg not in packages[family][device]:
|
|
print(f"Error: package '{pkg}' not listed for device '{device}'")
|
|
os.sys.exit()
|
|
pkg_name = f"{model}_{pkg}"
|
|
if model in ["xc7k"]:
|
|
m = re.match(r"(xc7k\d+t)(\w+)", part)
|
|
pkg = m.group(2)
|
|
pkg_name = f"{model}_{pkg}"
|
|
if tool == "ise":
|
|
model = m.group(1)
|
|
|
|
if tool in ["ise", "vivado"]:
|
|
pkg_name = {
|
|
"xc3s500evq100" : "xc3s_vq100",
|
|
"xc6slx9tqg144" : "xc6s_tqg144",
|
|
"xc6slx9csg324" : "xc6s_csg324",
|
|
"xc6slx16ftg256" : "xc6s_ftg256",
|
|
"xc6slx16csg324" : "xc6s_csg324",
|
|
"xc6slx25csg324" : "xc6s_csg324",
|
|
"xc6slx25tcsg324" : "xc6s_t_csg324",
|
|
"xc6slx45csg324" : "xc6s_csg324",
|
|
"xc6slx45tfgg484" : "xc6s_t_fgg484",
|
|
"xc6slx100fgg484" : "xc6s_fgg484",
|
|
"xc6slx150tcsg484" : "xc6s_csg484",
|
|
"xc6slx150tfgg484" : "xc6s_t_fgg484",
|
|
"xc6vlx130tff784" : "xc6v_ff784",
|
|
"xc7vx330tffg1157" : "xc7v_ffg1157",
|
|
"xcku040-ffva1156" : "xcku040_ffva1156",
|
|
"xcku060-ffva1156" : "xcku060_ffva1156",
|
|
"xcvu9p-flga2104" : "xcvu9p_flga2104",
|
|
"xcvu37p-fsvh2892" : "xcvu37p_fsvh2892",
|
|
"xcku3p-ffva676" : "xcku3p_ffva676",
|
|
"xcku3p-ffvb676" : "xcku3p_ffvb676",
|
|
"xcku5p-ffvb676" : "xcku5p_ffvb676",
|
|
"xcau7p-sbvc484" : "xcau7p_sbvc484",
|
|
"xcau10p-ffvb676" : "xcau10p_ffvb676",
|
|
"xcau15p-ffvb676" : "xcau15p_ffvb676",
|
|
}.get(part, pkg_name)
|
|
if tool == "ise":
|
|
cst_type = "UCF"
|
|
tool_options = {
|
|
"family": family,
|
|
"device": {
|
|
"xc3s500evq100" : "xc3s500e",
|
|
"xc6slx9tqg144" : "xc6slx9",
|
|
"xc6slx9csg324" : "xc6slx9",
|
|
"xc6slx16ftg256" : "xc6slx16",
|
|
"xc6slx16csg324" : "xc6slx16",
|
|
"xc6slx25csg324" : "xc6slx25",
|
|
"xc6slx25tcsg324" : "xc6slx25t",
|
|
"xc6slx45csg324" : "xc6slx45",
|
|
"xc6slx45tfgg484" : "xc6slx45t",
|
|
"xc6slx100fgg484" : "xc6slx100",
|
|
"xc6slx150tcsg484" : "xc6slx150t",
|
|
"xc6slx150tfgg484" : "xc6slx150t",
|
|
"xc6vlx130tff784" : "xc6vlx130t",
|
|
}.get(part, model),
|
|
"package": {
|
|
"xc3s500evq100" : "vq100",
|
|
"xc6slx9tqg144" : "tqg144",
|
|
"xc6slx9csg324" : "csg324",
|
|
"xc6slx16ftg256" : "ftg256",
|
|
"xc6slx16csg324" : "csg324",
|
|
"xc6slx25csg324" : "csg324",
|
|
"xc6slx25tcsg324" : "csg324",
|
|
"xc6slx45csg324" : "csg324",
|
|
"xc6slx45tfgg484" : "fgg484",
|
|
"xc6slx100fgg484" : "fgg484",
|
|
"xc6slx150tcsg484" : "csg484",
|
|
"xc6slx150tfgg484" : "fgg484",
|
|
"xc6vlx130tff784" : "ff784",
|
|
}.get(part, pkg),
|
|
"speed" : speed,
|
|
}
|
|
else:
|
|
cst_type = "xdc"
|
|
# Artix/Spartan 7 Specific use case:
|
|
if family in ["Artix", "Spartan 7"]:
|
|
tool_options = {"part": f"{device}{pkg}-1"}
|
|
elif family == "Xilinx UltraScale":
|
|
if part in ["xcvu9p-flga2104", "xcku5p-ffvb676"]:
|
|
tool_options = {"part": part + "-1-e"}
|
|
parameters["secondaryflash"] = {
|
|
"datatype": "int",
|
|
"paramtype": "vlogdefine",
|
|
"description": "secondary flash",
|
|
"default": 1,
|
|
}
|
|
elif part == "xcku3p-ffva676":
|
|
tool_options = {"part": part + "-2-e"}
|
|
elif part == "xcku3p-ffvb676":
|
|
tool_options = {"part": part + "-2-e"}
|
|
elif part == "xcvu37p-fsvh2892":
|
|
tool_options = {"part": part + "-2L-e"}
|
|
elif part in ["xcku040-ffva1156", "xcku060-ffva1156"]:
|
|
tool_options = {"part": part + "-2-e"}
|
|
parameters["secondaryflash"] = {
|
|
"datatype" : "int",
|
|
"paramtype" : "vlogdefine",
|
|
"description" : "secondary flash",
|
|
"default" : 1,
|
|
}
|
|
elif part in ["xcau10p-ffvb676", "xcau7p-sbvc484"]:
|
|
tool_options = {"part": part + "-1-e"}
|
|
elif part == "xcau15p-ffvb676":
|
|
tool_options = {"part": part + "-2-e"}
|
|
else:
|
|
tool_options = {"part": part + "-1"}
|
|
|
|
cst_file = currDir + "constr_" + pkg_name + "." + cst_type.lower()
|
|
files.append({"name": os.path.join(currDir, f"xilinx_{flash_type}OverJtag.v"),
|
|
"file_type": "verilogSource"})
|
|
files.append({"name": cst_file, "file_type": cst_type})
|
|
else:
|
|
# Altera only support SPI mode.
|
|
assert flash_type in ["spi"]
|
|
|
|
full_part = {
|
|
"10cl016484" : "10CL016YU484C8G",
|
|
"10cl025256" : "10CL025YU256C8G",
|
|
"10cl055484" : "10CL055YU484C8G",
|
|
"10cl006144" : "10CL006YE144C8G",
|
|
"ep4cgx15027" : "EP4CGX150DF27I7",
|
|
"ep4ce11523" : "EP4CE115F23C7",
|
|
"ep4ce2217" : "EP4CE22F17C6",
|
|
"ep4ce1523" : "EP4CE15F23C8",
|
|
"ep4ce1017" : "EP4CE10F17C8",
|
|
"ep4ce622" : "EP4CE6E22C8",
|
|
"5ce215" : "5CEBA2U15C8",
|
|
"5ce223" : "5CEFA2F23I7",
|
|
"5ce523" : "5CEFA5F23I7",
|
|
"5ce423" : "5CEBA4F23C8",
|
|
"5ce927" : "5CEBA9F27C7",
|
|
"5cse423" : "5CSEMA4U23C6",
|
|
"5cse623" : "5CSEBA6U23I7",
|
|
"5sgsd5" : "5SGSMD5K2F40I3",
|
|
}[part]
|
|
files.append({"name": currDir + "altera_spiOverJtag.v",
|
|
"file_type": "verilogSource"})
|
|
files.append({"name": currDir + "altclkctrl_inst.v",
|
|
"file_type": "verilogSource"})
|
|
files.append({"name": currDir + "altera_spiOverJtag.sdc",
|
|
"file_type": "SDC"})
|
|
tool_options = {"device": full_part, "family": family}
|
|
|
|
files.append({
|
|
"name" : os.path.join(currDir, f"{flash_type}OverJtag_core.v"),
|
|
"file_type" : "verilogSource",
|
|
})
|
|
|
|
parameters[family.lower().replace(" ", "")] = {
|
|
"datatype": "int",
|
|
"paramtype": "vlogdefine",
|
|
"description": "fpga family",
|
|
"default": 1
|
|
}
|
|
|
|
edam = {
|
|
"name" : f"{flash_type}OverJtag",
|
|
"files" : files,
|
|
"tool_options" : {tool: tool_options},
|
|
"parameters" : parameters,
|
|
"toplevel" : f"{flash_type}OverJtag",
|
|
}
|
|
|
|
backend = get_edatool(tool)(edam=edam, work_root=build_dir)
|
|
backend.configure()
|
|
backend.build()
|
|
|
|
if tool in ["vivado", "ise"]:
|
|
import gzip
|
|
import shutil
|
|
import subprocess
|
|
|
|
# Compress bitstream.
|
|
with open(f"tmp_{part}/{flash_type}OverJtag.bit", "rb") as bit:
|
|
with gzip.open(f"{flash_type}OverJtag_{part}.bit.gz", "wb", compresslevel=9) as bit_gz:
|
|
shutil.copyfileobj(bit, bit_gz)
|
|
|
|
# Create Symbolic links for all supported packages — only when building
|
|
# the bare device target (e.g. "xc7a100t"). When the explicit
|
|
# device+package form is used (e.g. "xc7a100tfgg676"), the bitstream is
|
|
# package-specific and must not be aliased to other packages.
|
|
if family in ["Artix", "Spartan 7"] and part in packages[family]:
|
|
in_file = f"{flash_type}OverJtag_{part}.bit.gz"
|
|
for pkg in packages[family][part]:
|
|
out_file = f"{flash_type}OverJtag_{part}{pkg}.bit.gz"
|
|
# Don't overwrite a package-specific bitstream with a symlink to
|
|
# the first-package build (this was the original bug).
|
|
if os.path.islink(out_file) or not os.path.exists(out_file):
|
|
if os.path.islink(out_file):
|
|
os.unlink(out_file)
|
|
subprocess.run(["ln", "-s", in_file, out_file])
|