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 1 commit
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]] = 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)}, {pins}, {bank_pins})
def xls_pinout(device):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this shouldn't be called xls when it's not excel based ;)

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
16 changes: 14 additions & 2 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):
import ipdb; ipdb.set_trace()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uh breakpoints should go I think haha

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly as predicted! So what's up with the nextpnr side? I mean, isn't the chipdb format the same?

The structure has changed: a table with packages has been added.
You can put the next patch on nexpnr

yrabbit/nextpnr@0e8a299

with b.block("partnumber_packages") as blk:
for partnumber, pkg_rec in db.packages.items():
pkg, device = pkg_rec
b.u32(id_string(partnumber))
b.u32(id_string(pkg))
b.u32(id_string(device))
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 @@ -189,6 +200,7 @@ def write_chipdb(db, f, device):
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
119 changes: 71 additions & 48 deletions apycula/pindef.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,88 @@
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)}
def all_packages(device):
gowinhome = os.getenv("GOWINHOME")
if not gowinhome:
raise Exception("GOWINHOME not set")
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)
_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")]


17 changes: 6 additions & 11 deletions apycula/tiled_fuzzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +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",
"package": "LQFP144",
"header": 1, # stupid note in excel
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this header can go

"device": "GW1N-1-LQFP144-6",
"partnumber": "GW1N-LV1LQ144C6/I5",
Expand Down Expand Up @@ -464,9 +461,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.xls_pinout(device)

corners = [
(0, 0, fse['header']['grid'][61][0][0]),
Expand All @@ -480,7 +475,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
2 changes: 1 addition & 1 deletion apycula/tm_h4x.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ def read_tm(f, device):
try:
speed_class = chunk_order[i]
except IndexError:
speed_class = i
speed_class = str(i)
tmdat[speed_class] = {}
assert len(chunk) == chunklen
res = parse_chunk(chunk)
Expand Down
2 changes: 1 addition & 1 deletion examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ clean:
gowin_pack -d GW1N-9 -o $@ $<

%-tec0117.json: %-tec0117-synth.json tec0117.cst
$(NEXTPNR) --json $< --write $@ --device GW1NR-UV9QN881C6/I5 --cst tec0117.cst
$(NEXTPNR) --json $< --write $@ --device GW1NR-UV9QN88C6/I5 --cst tec0117.cst

%-runber.fs: %-runber.json
gowin_pack -d GW1N-4 -o $@ $<
Expand Down