Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get info about packages/pins from the IDE files. #57

Merged
merged 6 commits into from
Nov 6, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 54 additions & 43 deletions apycula/chipdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class Device:
# a grid of tiles
grid: List[List[Tile]] = field(default_factory=list)
timing: Dict[str, Dict[str, List[float]]] = field(default_factory=dict)
packages: Dict[str, Tuple[str, str, str]] = field(default_factory=dict)
pinout: Dict[str, Dict[str, Dict[str, str]]] = field(default_factory=dict)
pin_bank: Dict[str, int] = field(default_factory = dict)
cmd_hdr: List[ByteString] = field(default_factory=list)
Expand Down Expand Up @@ -234,51 +235,57 @@ def from_fse(fse):
return dev

def get_pins(device):
if device not in {"GW1N-1", "GW1N-4", "GW1N-9", "GW1NR-9", "GW1NS-2", "GW1NS-2C"}:
raise Exception("unsupported device")
pkgs = pindef.all_packages(device)
res = {}
res_bank_pins = {}
for pkg_rec in pkgs.values():
pkg = pkg_rec[0]
if pkg in res:
continue
res[pkg] = pindef.get_pin_locs(device, pkg, pindef.VeryTrue)
res_bank_pins.update(pindef.get_bank_pins(device, pkg))
return (pkgs, res, res_bank_pins)

# returns ({partnumber: (package, device, speed)}, {pins}, {bank_pins})
def json_pinout(device):
if device == "GW1N-1":
header = 1
start = 5
pkgs, pins, bank_pins = get_pins("GW1N-1")
return (pkgs, {
"GW1N-1": pins
}, bank_pins)
elif device == "GW1N-4":
header = 0
start = 7
pkgs, pins, bank_pins = get_pins("GW1N-4")
return (pkgs, {
"GW1N-4": pins
}, bank_pins)
elif device == "GW1N-9":
header = 0
start = 7
elif device == "GW1NR-9":
header = 1
start = 7
pkgs, pins, bank_pins = get_pins("GW1N-9")
pkgs_r, pins_r, bank_pins_r = get_pins("GW1NR-9")
res = {}
res.update(pkgs)
res.update(pkgs_r)
res_bank_pins = {}
res_bank_pins.update(bank_pins)
res_bank_pins.update(bank_pins_r)
return (res, {
"GW1N-9": pins,
"GW1NR-9": pins_r
}, res_bank_pins)
elif device == "GW1NS-2":
header = 0
start = 7
elif device == "GW1NS-2C":
header = 0
start = 7
else:
raise Exception("unsupported device")
pkgs = pindef.all_packages(device, start, header)
res = {}
for pkg in pkgs:
res[pkg] = pindef.get_pin_locs(device, pkg, pindef.VeryTrue, header)
return res

def xls_pinout(family):
if family == "GW1N-1":
return {
"GW1N-1": get_pins("GW1N-1"),
}
elif family == "GW1N-9":
return {
"GW1N-9": get_pins("GW1N-9"),
"GW1NR-9": get_pins("GW1NR-9"),
}
elif family == "GW1N-4":
return {
"GW1N-4": get_pins("GW1N-4"),
}
elif family == "GW1NS-2":
return {
"GW1NS-2": get_pins("GW1NS-2"),
"GW1NS-2C": get_pins("GW1NS-2C"),
}
pkgs, pins, bank_pins = get_pins("GW1NS-2")
pkgs_c, pins_c, bank_pins_c = get_pins("GW1NS-2C")
res = {}
res.update(pkgs)
res.update(pkgs_c)
res_bank_pins = {}
res_bank_pins.update(bank_pins)
res_bank_pins.update(bank_pins_c)
return (res, {
"GW1NS-2": pins,
"GW1NS-2C": pins_c
}, res_bank_pins)
else:
raise Exception("unsupported device")

Expand Down Expand Up @@ -487,7 +494,11 @@ def loc2bank(db, row, col):
"""
bank = db.corners.get((row, col))
if bank == None:
# XXX do something with this 'A'
bank = db.pin_bank[loc2pin_name(db, row, col) + 'A']
name = loc2pin_name(db, row, col)
nameA = name + 'A'
if nameA in db.pin_bank:
bank = db.pin_bank[nameA]
else:
bank = db.pin_bank[name + 'B']
return bank

5 changes: 3 additions & 2 deletions apycula/clock_fuzzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ def ibuf(mod, cst, loc, clk=None):
with open(f"{tiled_fuzzer.device}_stage1.pickle", 'rb') as f:
db = pickle.load(f)

# init pindef
pindef.all_packages(tiled_fuzzer.device)

clock_pins = pindef.get_clock_locs(
tiled_fuzzer.device,
tiled_fuzzer.params['package'],
header=tiled_fuzzer.params['header'])
tiled_fuzzer.params['package'])
# pins appear to be differential with T/C denoting true/complementary
true_pins = [p[0] for p in clock_pins if "GCLKT" in p[1]]

Expand Down
18 changes: 15 additions & 3 deletions apycula/gowin_bba.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from apycula import chipdb

class Bba(object):

def __init__(self, file):
self.file = file
self.block_idx = Counter()
Expand All @@ -20,7 +20,7 @@ def write_value(val):

def str(self, val, sep="|"):
self.file.write(f"str {sep}{val}{sep}\n")

@contextmanager
def block(self, prefix="block"):
idx = self.block_idx[prefix]
Expand Down Expand Up @@ -147,6 +147,17 @@ def write_timing(b, timing):
b.u32(len(timing))
b.ref(blk)

def write_partnumber_packages(b, db):
with b.block("partnumber_packages") as blk:
for partnumber, pkg_rec in db.packages.items():
pkg, device, speed = pkg_rec
b.u32(id_string(partnumber))
b.u32(id_string(pkg))
b.u32(id_string(device))
b.u32(id_string(speed))
b.u32(len(db.packages))
b.ref(blk)

pin_re = re.compile(r"IO([TBRL])(\d+)([A-Z])")
def iob2bel(db, name):
banks = {'T': [(1, n) for n in range(1, db.cols)],
Expand Down Expand Up @@ -183,12 +194,13 @@ def write_chipdb(db, f, device):
b.pre('NEXTPNR_NAMESPACE_BEGIN')
with b.block(f'chipdb_{cdev}') as blk:
b.str(device)
b.u32(0) # version
b.u32(1) # version
b.u16(db.rows)
b.u16(db.cols)
write_grid(b, db.grid)
write_global_aliases(b, db)
write_timing(b, db.timing)
write_partnumber_packages(b, db)
write_pinout(b, db)
id_strings(b)
b.post(f'EmbeddedFile chipdb_file_{cdev}("gowin/chipdb-{device}.bin", {blk});')
Expand Down
3 changes: 1 addition & 2 deletions apycula/gowin_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,7 @@ def place(db, tilemap, bels, cst):
continue
if flag_name_val[0] == chipdb.mode_attr_sep + "IO_TYPE":
if iostd and iostd != flag_name_val[1]:
raise Exception("Different I/O modes for the same bank were specified: " +
f"{iostd} and {flag_name_val[1]}")
raise Exception(f"Different I/O modes for the same bank {bank} were specified: {iostd} and {flag_name_val[1]}")
iostd = iostd_alias.get(flag_name_val[1], flag_name_val[1])

# first used pin sets bank's iostd
Expand Down
128 changes: 80 additions & 48 deletions apycula/pindef.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,97 @@
import pandas as pd
from os.path import expanduser
from glob import glob

docdir = expanduser("~/Documents/gowinsemi/")
files = glob(docdir+"*Pinout.xlsx")
import json
import os
import csv

VeryTrue = 2

def get_package(series, package, special_pins, header):
fname = None
for f in files:
if "%s Pinout" % series in f:
fname = f
break
assert fname, "No file found for {}".format(series)
# caches
# .CSV index of vendor files {(device, package) : file_name}
_pindef_index = {}
# (device, package) : pins
_pindef_files = {}

def get_package(device, package, special_pins):
global _pindef_files
if (device, package) not in _pindef_files:
gowinhome = os.getenv("GOWINHOME")
if not gowinhome:
raise Exception("GOWINHOME not set")
with open(_pindef_index[(device, package)]) as f:
pins = json.load(f)
_pindef_files[(device, package)] = [d for d in pins['PIN_DATA'] if d['TYPE'] == 'I/O']

df = pd.read_excel(fname, sheet_name="Pin List", header=header, engine='openpyxl')
df = df.dropna(subset=[package])
df = df[df['Function']=="I/O"]
if special_pins != VeryTrue:
df = df[df["Configuration Function"] != "RECONFIG_N"] # can't be output
df = df[~df["Configuration Function"].str.startswith("JTAGSEL_N", na=False)] # dedicated pin
pins = [pin for pin in _pindef_files[(device, package)]
if 'CFG' not in pin.keys() or (
pin['CFG'] != 'RECONFIG_N' and not pin['CFG'].startswith('JTAGSEL_N'))]
else:
pins = _pindef_files[(device, package)]
if not special_pins:
df = df[df["Configuration Function"].isna()]
return df

def all_packages(series, start, header):
df = get_package(series, "Pin Name", True, header)
return list(df.columns[start:])
return [pin for pin in pins if 'CFG' not in pin.keys()]
return pins

def get_pins(series, package, special_pins=False, header=0):
df = get_package(series, package, special_pins, header)
df = df[["BANK", package]].astype("int32")
return df.groupby("BANK")[package].apply(list).to_dict()
# {partnumber : (pkg, device, speed)}
def all_packages(device):
gowinhome = os.getenv("GOWINHOME")
if not gowinhome:
raise Exception("GOWINHOME not set")
# {package: speed} vendor file
speeds = {}
with open(f"{gowinhome}/IDE/data/device/device_info.csv", mode='r') as csv_file:
csv_reader = csv.DictReader(csv_file, fieldnames =
["unused_id", "partnumber", "series", "device", "package", "voltage", "speed"])
for row in csv_reader:
if row['device'] != device:
continue
speeds.update({row['partnumber']: row['speed']})
global _pindef_index
_pindef_index = {}
res = {}
with open(f"{gowinhome}/IDE/data/device/device_package.csv", mode='r') as csv_file:
csv_reader = csv.DictReader(csv_file, fieldnames =
["unused_id", "partnumber", "series", "device", "package", "filename"])
for row in csv_reader:
if row['device'] != device:
continue
res[row['partnumber']] = (row['package'], device, speeds[row['partnumber']])
_pindef_index[(row['device'], row['package'])] = \
f"{gowinhome}/IDE/data/device/{row['filename']}"
return res

def get_bank_pins(series, header = 0):
df = get_package(series, "Pin Name", VeryTrue, header)
# bad table for GW1N-1
if series == "GW1N-1":
df.loc[df['Pin Name'] == 'IOB2B', ['BANK']] = 2
dpins = list(map(lambda x:x.split('/')[0], df['Pin Name'].to_list()))
dbanks = df['BANK'].astype("int32").to_list()
return dict(zip(dpins, dbanks))
def get_pins(device, package, special_pins=False):
df = get_package(device, package, special_pins)
res = {}
for pin in df:
res.setdefault(str(pin['BANK']), []).append(str(pin['INDEX']))
return res

def get_locs(series, package, special_pins=False, header=0):
df = get_package(series, package, special_pins, header)
return {p.split('/')[0] for p in df["Pin Name"]}
def get_bank_pins(device, package):
df = get_package(device, package, VeryTrue)
res = {}
for pin in df:
res[pin['NAME']] = str(pin['BANK'])
return res

def get_pin_locs(series, package, special_pins=False, header=0):
def tryint(n):
try:
return int(n)
except:
return n
def get_locs(device, package, special_pins=False):
df = get_package(device, package, special_pins)
res = set()
for pin in df:
res.update({pin['NAME']})
return res

df = get_package(series, package, special_pins, header)
return {tryint(num): p.split('/')[0] for _, num, p in df[[package, "Pin Name"]].itertuples()}
def get_pin_locs(device, package, special_pins=False):
df = get_package(device, package, special_pins)
res = {}
for pin in df:
res[str(pin['INDEX'])] = pin['NAME']
return res

def get_clock_locs(series, package, header=0):
df = get_package(series, package, True, header)
df = df[df["Configuration Function"].str.startswith("GCLK", na=False)]
return {tuple(p.split('/')) for p in df["Pin Name"]}
def get_clock_locs(device, package):
df = get_package(device, package, True)
return [(pin['NAME'], *pin['CFG'].split('/')) for pin in df
if 'CFG' in pin.keys() and pin['CFG'].startswith("GCLK")]


18 changes: 6 additions & 12 deletions apycula/tiled_fuzzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,22 @@

params = {
"GW1NS-2": {
"package": "LQ144",
"header": 0,
"package": "LQFP144",
"device": "GW1NS-2C-LQ144-5",
"partnumber": "GW1NS-UX2CLQ144C5/I4",
},
"GW1N-9": {
"package": "PG256",
"header": 0,
"package": "PBGA256",
"device": "GW1N-9-PBGA256-6",
"partnumber": "GW1N-LV9PG256C6/I5",
},
"GW1N-4": {
"package": "PG256",
"header": 0,
"package": "PBGA256",
"device": "GW1N-4-PBGA256-6",
"partnumber": "GW1N-LV4PG256C6/I5",
},
"GW1N-1": {
"package": "LQ144",
"header": 1, # stupid note in excel
"package": "LQFP144",
"device": "GW1N-1-LQFP144-6",
"partnumber": "GW1N-LV1LQ144C6/I5",
},
Expand Down Expand Up @@ -464,9 +460,7 @@ def run_pnr(mod, constr, config):

db = chipdb.from_fse(fse)
db.timing = tm
db.pinout = chipdb.xls_pinout(device)
# pin <-> bank
db.pin_bank = pindef.get_bank_pins(device, params['header'])
db.packages, db.pinout, db.pin_bank = chipdb.json_pinout(device)

corners = [
(0, 0, fse['header']['grid'][61][0][0]),
Expand All @@ -480,7 +474,7 @@ def run_pnr(mod, constr, config):
for col, typ in enumerate(row_dat):
locations.setdefault(typ, []).append((row, col))

pin_names = pindef.get_locs(device, params['package'], True, params['header'])
pin_names = pindef.get_locs(device, params['package'], True)
edges = {'T': fse['header']['grid'][61][0],
'B': fse['header']['grid'][61][-1],
'L': [row[0] for row in fse['header']['grid'][61]],
Expand Down
Loading