Skip to content

Commit

Permalink
Merge pull request #280 from YosysHQ/sdram
Browse files Browse the repository at this point in the history
add magic sdram pin constraints
  • Loading branch information
yrabbit authored Oct 10, 2024
2 parents 4f87247 + 2d86ebe commit 1b07993
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 6 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ all: apycula/GW1N-1.pickle apycula/GW1N-9.pickle apycula/GW1N-4.pickle \
%_stage2.pickle: apycula/clock_fuzzer.py %_stage1.pickle
python3 -m apycula.clock_fuzzer $*

apycula/%.pickle: %_stage2.pickle
%_stage3.pickle: apycula/find_sdram_pins.py %_stage2.pickle
python3 -m apycula.find_sdram_pins $*

apycula/%.pickle: %_stage3.pickle
gzip -c $< > $@

clean:
Expand Down
2 changes: 2 additions & 0 deletions apycula/chipdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class Device:
packages: Dict[str, Tuple[str, str, str]] = field(default_factory=dict)
# {variant: {package: {pin#: (pin_name, [cfgs])}}}
pinout: Dict[str, Dict[str, Dict[str, Tuple[str, List[str]]]]] = field(default_factory=dict)
# {variant: {package: (net, row, col, AB, iostd)}}
sip_cst: Dict[str, Dict[str, Tuple[str, int, int, str, str]]] = field(default_factory=dict)
pin_bank: Dict[str, int] = field(default_factory = dict)
cmd_hdr: List[ByteString] = field(default_factory=list)
cmd_ftr: List[ByteString] = field(default_factory=list)
Expand Down
8 changes: 4 additions & 4 deletions apycula/codegen.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from itertools import chain
import re

class Module:
def __init__(self):
Expand Down Expand Up @@ -26,7 +27,8 @@ def write(self, f):
if not first:
f.write(", ")
first = False
f.write(port)
bare = re.sub(r" *\[.*\] *", "", port)
f.write(bare)
f.write(");\n")

for port in self.inputs:
Expand Down Expand Up @@ -134,9 +136,7 @@ def write(self, f):
run pnr
"""

device_desc = self.partnumber
if self.device in ['GW1N-9', 'GW1N-4', 'GW1N-9C', 'GW2A-18', 'GW2A-18C']:
device_desc = f'-name {self.device} {device_desc}'
device_desc = f'-name {self.device} {self.partnumber}'

f.write(template.format(
cst=self.cst,
Expand Down
160 changes: 160 additions & 0 deletions apycula/find_sdram_pins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/usr/bin/python3

import pickle
from multiprocessing.dummy import Pool

from apycula import codegen, tiled_fuzzer, gowin_unpack, chipdb

def run_script(pinName : str, idx=None, iostd=None):
if idx == None:
port = pinName
idxName = pinName
else:
port = f"[{idx}:{idx}] {pinName}"
idxName = f"{pinName}[{idx}]"

mod = codegen.Module()
cst = codegen.Constraints()

name = "iobased"
if pinName.startswith("IO"):
mod.inouts.add(port)
iob = codegen.Primitive("TBUF", name)
iob.portmap["O"] = idxName
iob.portmap["I"] = ""
iob.portmap["OEN"] = ""
mod.primitives[name] = iob
elif pinName.startswith("O"):
mod.outputs.add(port)
iob = codegen.Primitive("OBUF", name)
iob.portmap["O"] = idxName
iob.portmap["I"] = ""
mod.primitives[name] = iob
elif pinName.startswith("I"):
mod.inputs.add(port)
iob = codegen.Primitive("IBUF", name)
iob.portmap["I"] = idxName
iob.portmap["O"] = ""
mod.primitives[name] = iob
else:
raise ValueError(pinName)

pnr_result = tiled_fuzzer.run_pnr(mod, cst, {})
tiles = chipdb.tile_bitmap(db, pnr_result.bitmap)

for (i, j), tile in tiles.items():
bels, _, _ = gowin_unpack.parse_tile_(db, i, j, tile)
iob_location = f"R{i}C{j}"
print(iob_location, bels)
if bels and ("IOBA" in bels or "IOBB" in bels):
bel = next(iter(bels))
belname = tiled_fuzzer.rc2tbrl(db, i+1, j+1, bel[-1])
print(db.rows, db.cols)
print(idxName, belname)
return (idxName, i, j, bel[-1], iostd)

raise Exception(f"No IOB found for {port}")

# these are all system in package variants with magic wire names connected interally
# pin names are obtained from litex-boards
# (pin name, bus width, iostd)
params = {
"GW1N-9": [{
"package": "QFN88",
"device": "GW1NR-9",
"partnumber": "GW1NR-UV9QN88C6/I5",
"pins": [
("IO_sdram_dq", 16, "LVCMOS33"),
("O_sdram_clk", 0, "LVCMOS33"),
("O_sdram_cke", 0, "LVCMOS33"),
("O_sdram_cs_n", 0, "LVCMOS33"),
("O_sdram_cas_n", 0, "LVCMOS33"),
("O_sdram_ras_n", 0, "LVCMOS33"),
("O_sdram_wen_n", 0, "LVCMOS33"),
("O_sdram_addr", 12, "LVCMOS33"),
("O_sdram_dqm", 2, "LVCMOS33"),
("O_sdram_ba", 2, "LVCMOS33")
],
}],
"GW1N-9C": [{
"package": "QFN88P",
"device": "GW1NR-9C",
"partnumber": "GW1NR-LV9QN88PC6/I5",
"pins": [
("O_psram_ck", 2, None),
("O_psram_ck_n", 2, None),
("O_psram_cs_n", 2, None),
("O_psram_reset_n", 2, None),
("IO_psram_dq", 16, None),
("IO_psram_rwds", 2, None),
],
}],
"GW1NS-4": [{
"package": "QFN48",#??
"device": "GW1NSR-4C",
"partnumber": "GW1NSR-LV4CQN48PC7/I6",
"pins": [
("O_hpram_ck", 2, None),
("O_hpram_ck_n", 2, None),
("O_hpram_cs_n", 2, None),
("O_hpram_reset_n", 2, None),
("IO_hpram_dq", 16, None),
("IO_hpram_rwds", 2, None),
],
}],
"GW2A-18C": [{
"package": "QFN88",
"device": "GW2AR-18C",
"partnumber": "GW2AR-LV18QN88C8/I7",
"pins": [
("O_sdram_clk", 0, "LVCMOS33"),
("O_sdram_cke", 0, "LVCMOS33"),
("O_sdram_cs_n", 0, "LVCMOS33"),
("O_sdram_cas_n", 0, "LVCMOS33"),
("O_sdram_ras_n", 0, "LVCMOS33"),
("O_sdram_wen_n", 0, "LVCMOS33"),
("O_sdram_dqm", 4, "LVCMOS33"),
("O_sdram_addr", 11, "LVCMOS33"),
("O_sdram_ba", 2, "LVCMOS33"),
("IO_sdram_dq", 32, "LVCMOS33"),
]
}],
}

with open(f"{tiled_fuzzer.device}_stage2.pickle", 'rb') as f:
db = pickle.load(f)

pool = Pool()

if tiled_fuzzer.device in params:
devices = params[tiled_fuzzer.device]
for device in devices:
tiled_fuzzer.params = device
pins = device["pins"]

runs = []
for pinName, pinBuswidth, iostd in pins:
if (pinBuswidth == 0):
runs.append((pinName, None, iostd))
else:
for pinIdx in range(0, pinBuswidth):
runs.append((pinName, pinIdx, iostd))

print(runs)

pinmap = pool.map(lambda params: run_script(*params), runs)

# check for duplicates
seen = {}
for pin in pinmap:
wire = pin[0]
bel = pin[1:4]
if bel in seen:
print("WARNING:", wire, "conflicts with", seen[bel], "at", bel)
else:
seen[bel] = wire

db.sip_cst.setdefault(device["device"], {})[device["package"]] = pinmap

with open(f"{tiled_fuzzer.device}_stage3.pickle", 'wb') as f:
pickle.dump(db, f)
13 changes: 13 additions & 0 deletions apycula/tiled_fuzzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ def tbrl2rc(fse, side, num):
col = len(fse['header']['grid'][61][0])-1
return (row, col)

def rc2tbrl(db, row, col, num):
edge = 'T'
idx = col
if row == db.rows:
edge = 'B'
elif col == 1:
edge = 'L'
idx = row
elif col == db.cols:
edge = 'R'
idx = row
return f"IO{edge}{idx}{num}"

# Read the packer vendor log to identify problem with primitives/attributes
# returns dictionary {(primitive name, error code) : [full error text]}
_err_parser = re.compile(r"(\w+) +\(([\w\d]+)\).*'(inst[^\']+)\'.*")
Expand Down
2 changes: 1 addition & 1 deletion examples/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
YOSYS ?= yosys
NEXTPNR ?= nextpnr-gowin
NEXTPNR ?= nextpnr-gowin --timing-allow-fail

.DEFAULT_GOAL := all

Expand Down

0 comments on commit 1b07993

Please sign in to comment.