Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: gatecat <[email protected]>
  • Loading branch information
gatecat committed Aug 28, 2023
1 parent e08471d commit 36a723d
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 22 deletions.
10 changes: 1 addition & 9 deletions himbaechel/chipdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,6 @@ NPNR_PACKED_STRUCT(struct TimingValue {
int32_t slow_max;
});

NPNR_PACKED_STRUCT(struct BelPinTimingPOD {
TimingValue in_cap;
TimingValue drive_res;
TimingValue delay;
});

NPNR_PACKED_STRUCT(struct PipTimingPOD {
TimingValue int_delay;
TimingValue in_cap;
Expand Down Expand Up @@ -191,14 +185,12 @@ NPNR_PACKED_STRUCT(struct CellPinTimingPOD {
});

NPNR_PACKED_STRUCT(struct CellTimingPOD {
int32_t type;
int32_t variant;
int32_t type_variant;
RelSlice<CellPinTimingPOD> pins;
});

NPNR_PACKED_STRUCT(struct SpeedGradePOD {
int32_t name;
RelSlice<BelPinTimingPOD> bel_pin_classes;
RelSlice<PipTimingPOD> pip_classes;
RelSlice<NodeTimingPOD> node_classes;
RelSlice<CellTimingPOD> cell_types;
Expand Down
3 changes: 3 additions & 0 deletions himbaechel/himbaechel_dbgen/bba.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ def str(self, s, comment=""):
def label(self, s):
print(f"label {s}", file=self.f)
def u8(self, n, comment=""):
assert n is int
print(f"u8 {n} {comment}", file=self.f)
def u16(self, n, comment=""):
assert n is int
print(f"u16 {n} {comment}", file=self.f)
def u32(self, n, comment=""):
assert n is int
print(f"u32 {n} {comment}", file=self.f)
def pop(self):
print("pop", file=self.f)
242 changes: 233 additions & 9 deletions himbaechel/himbaechel_dbgen/chip.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def serialise(self, context: str, bba: BBAWriter):
@dataclass
class TileType(BBAStruct):
strs: StringPool
tmg: TimingPool
type_name: IdString
bels: list[BelData] = field(default_factory=list)
pips: list[PipData] = field(default_factory=list)
Expand Down Expand Up @@ -223,11 +224,12 @@ def create_wire(self, name: str, type: str=""):
self._wire2idx[wire.name] = wire.index
self.wires.append(wire)
return wire
def create_pip(self, src: str, dst: str):
def create_pip(self, src: str, dst: str, timing_class: str=""):
# Create a pip between two tile wires in the tile type. Both wires should exist already.
src_idx = self._wire2idx[self.strs.id(src)]
dst_idx = self._wire2idx[self.strs.id(dst)]
pip = PipData(index=len(self.pips), src_wire=src_idx, dst_wire=dst_idx)
pip = PipData(index=len(self.pips), src_wire=src_idx, dst_wire=dst_idx,
timing_idx=self.tmg.pip_class_idx(timing_class))
self.wires[src_idx].pips_downhill.append(pip.index)
self.wires[dst_idx].pips_uphill.append(pip.index)
self.pips.append(pip)
Expand Down Expand Up @@ -291,6 +293,8 @@ def serialise(self, context: str, bba: BBAWriter):
@dataclass
class NodeShape(BBAStruct):
wires: list[TileWireRef] = field(default_factory=list)
timing_index: int = -1

def key(self):
m = hashlib.sha1()
for wire in self.wires:
Expand All @@ -307,7 +311,7 @@ def serialise_lists(self, context: str, bba: BBAWriter):
bba.u16(0) # alignment
def serialise(self, context: str, bba: BBAWriter):
bba.slice(f"{context}_wires", len(self.wires))
bba.u32(-1) # timing index (not yet used)
bba.u32(timing_index) # timing index (not yet used)

MODE_TILE_WIRE = 0x7000
MODE_IS_ROOT = 0x7001
Expand Down Expand Up @@ -371,8 +375,225 @@ def serialise(self, context: str, bba: BBAWriter):
bba.ref(f"{context}_extra_data")
else:
bba.u32(0)

class TimingValue(BBAStruct):
def __init__(self, fast_min=0, fast_max=None, slow_min=None, slow_max=None):
self.fast_min = fast_min
self.fast_max = fast_max or fast_min
self.slow_min = slow_min or self.fast_min
self.slow_max = slow_max or self.fast_max

def serialise_lists(self, context: str, bba: BBAWriter):
pass
def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.fast_min)
bba.u32(self.fast_max)
bba.u32(self.slow_min)
bba.u32(self.slow_max)

@dataclass
class PipTiming(BBAStruct):
int_delay: TimingValue = field(default_factory=TimingValue) # internal fixed delay in ps
in_cap: TimingValue = field(default_factory=TimingValue) # internal capacitance in notional femtofarads
out_res: TimingValue = field(default_factory=TimingValue) # drive/output resistance in notional milliohms
flags: int = 0 # is_buffered etc
def serialise_lists(self, context: str, bba: BBAWriter):
pass
def serialise(self, context: str, bba: BBAWriter):
self.int_delay.serialise(context, bba)
self.in_cap.serialise(context, bba)
self.out_res.serialise(context, bba)
bba.u32(self.flags)

@dataclass
class NodeTiming(BBAStruct):
res: TimingValue = field(default_factory=TimingValue) # wire resistance in notional milliohms
cap: TimingValue = field(default_factory=TimingValue) # wire capacitance in notional femtofarads
delay: TimingValue = field(default_factory=TimingValue) # fixed wire delay in ps
def serialise_lists(self, context: str, bba: BBAWriter):
pass
def serialise(self, context: str, bba: BBAWriter):
self.res.serialise(context, bba)
self.cap.serialise(context, bba)
self.delay.serialise(context, bba)

@dataclass
class ClockEdge(Enum):
RISING = 0
FALLING = 1

@dataclass
class CellPinRegArc(BBAStruct):
clock: int
edge: ClockEdge
setup: TimingValue = field(default_factory=TimingValue) # setup time in ps
hold: TimingValue = field(default_factory=TimingValue) # hold time in ps
clk_q: TimingValue = field(default_factory=TimingValue) # clock to output time in ps
def serialise_lists(self, context: str, bba: BBAWriter):
pass
def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.clock)
bba.u32(self.edge.index)
self.setup.serialise(context, bba)
self.hold.serialise(context, bba)
self.clk_q.serialise(context, bba)

@dataclass
class CellPinCombArc(BBAStruct):
from_pin: int
delay: TimingValue = field(default_factory=TimingValue)
def serialise_lists(self, context: str, bba: BBAWriter):
pass
def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.from_pin)
self.delay.serialise(context, bba)

@dataclass
class CellPinTiming(BBAStruct):
pin: int
comb_arcs: list[CellPinCombArc] = field(default_factory=list) # sorted by from_pin ID index
reg_arcs: list[CellPinRegArc] = field(default_factory=list) # sorted by clock ID index
def finalise(self):
self.comb_arcs.sort(key=lambda a: a.from_pin)
self.reg_arcs.sort(key=lambda a: a.clock)

def serialise_lists(self, context: str, bba: BBAWriter):
bba.label(f"{context}_comb")
for i, a in enumerate(self.comb_arcs):
a.serialise(f"{context}_comb{i}", bba)
bba.label(f"{context}_reg")
for i, a in enumerate(self.reg_arcs):
a.serialise(f"{context}_reg{i}", bba)
def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.pin) # pin idstring
bba.slice(f"{context}_comb", len(self.comb_arcs))
bba.slice(f"{context}_reg", len(self.reg_arcs))

class CellTiming(BBAStruct):
def __init__(self, strs: StringPool, type_variant: str):
self.strs = strs
self.type_variant = strs.id(type_variant)
self.pin_data = {}

# combinational timing through a cell (like a LUT delay)
def add_comb_arc(self, from_pin: str, to_pin: str, delay: TimingValue):
if to_pin not in self.pin_data:
self.pin_data[to_pin] = CellPinTiming(pin=self.strs.id(to_pin))
self.pin_data[to_pin].comb_arcs.append(CellPinCombArc(from_pin=self.strs.id(from_pin), delay=delay))

# register input style timing (like a DFF input)
def add_setup_hold(self, clock: str, input_pin: str, edge: ClockEdge, setup: TimingValue, hold: TimingValue):
if input_pin not in self.pin_data:
self.pin_data[input_pin] = CellPinTiming(pin=self.strs.id(input_pin))
self.pin_data[input_pin].reg_arcs.append(CellPinRegArc(clock=self.strs.id(clock), edge=edge, setup=setup, hold=hold))

# register output style timing (like a DFF output)
def add_clock_out(self, clock: str, output_pin: str, edge: ClockEdge, delay: TimingValue):
if output_pin not in self.pin_data:
self.pin_data[output_pin] = CellPinTiming(pin=self.strs.id(output_pin))
self.pin_data[output_pin].reg_arcs.append(CellPinRegArc(clock=self.strs.id(clock), edge=edge, clk_q=delay))

def finalise(self):
self.pins = list(self.pin_data.values())
self.pins.sort(key=lambda p: p.pin)
for pin in self.pins:
pin.finalise()

def serialise_lists(self, context: str, bba: BBAWriter):
bba.label(f"{context}_pins")
for i, p in enumerate(self.pins):
p.serialise(f"{context}_pin{i}", bba)
def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.type_variant) # pin idstring
bba.slice(f"{context}_pins", len(self.pins))

@dataclass
class SpeedGrade(BBAStruct):
name: int
pip_classes: list[PipTiming|None] = field(default_factory=list)
node_classes: list[NodeTiming|None] = field(default_factory=list)
cell_types: list[CellTiming] = field(default_factory=list) # sorted by (cell_type, variant) ID tuple

def finalise(self):
self.cell_types.sort(key=lambda ty: ty.type_variant)
for ty in self.cell_types:
ty.finalise()

def serialise_lists(self, context: str, bba: BBAWriter):
bba.label(f"{context}_pip_classes")
for i, p in enumerate(self.pip_classes):
p.serialise(f"{context}_pipc{i}", bba)
bba.label(f"{context}_node_classes")
for i, n in enumerate(self.node_classes):
n.serialise(f"{context}_nodec{i}", bba)
bba.label(f"{context}_cell_types")
for i, t in enumerate(self.cell_types):
t.serialise(f"{context}_cellty{i}", bba)
def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.name) # pin idstring
bba.slice(f"{context}_pip_classes", len(self.pip_classes))
bba.slice(f"{context}_node_classes", len(self.node_classes))
bba.slice(f"{context}_cell_types", len(self.cell_types))

class TimingPool(BBAStruct):
def __init__(self, strs: StringPool, speed_grades: list):
self.strs = strs
self.speed_grades = [SpeedGrade(name=strs.id(g)) for g in speed_grades]
self.speed_grade_idx = {g: i for i, g in enumerate(speed_grades)}
self.pip_classes = {"": -1}
self.node_classes = {"": -1}

def pip_class_idx(self, name: str):
if name == "":
return -1
elif name in self.pip_classes:
return self.pip_classes[name]
else:
idx = len(self.pip_classes)
self.pip_classes[name] = idx
return idx

def node_class_idx(self, name: str):
if name == "":
return -1
elif name in self.node_classes:
return self.node_classes[name]
else:
idx = len(self.node_classes)
self.node_classes[name] = idx
return idx

def set_pip_class(self, grade: str, name: str, delay: TimingValue,
in_cap: Optional[TimingValue]=None, out_res: Optional[TimingValue]=None,
is_buffered=True):
idx = self.pip_class_idx(name)
grade = self.speed_grades[self.speed_grade_idx[grade]]
if idx >= len(grade.pip_classes):
grade.pip_classes += [None for i in range(idx - len(grade.pip_classes))]
assert grade.pip_classes[idx] is None, f"attempting to set pip class {name} to speed grade {grade} twice"
grade.pip_classes[idx] = PipTiming(int_delay=delay, in_cap=in_cap, out_res=out_res, flags=(1 if is_buffered else 0))

def set_bel_pin_class(self, grade: str, name: str, delay: TimingValue,
in_cap: Optional[TimingValue]=None, out_res: Optional[TimingValue]=None):
# bel pin classes are shared with pip classes, but this alias adds a bit of extra clarity
set_pip_class(self, grade, name, delay, in_cap, out_res, is_buffered=True)

def set_node_class(self, grade: str, name: str, delay: TimingValue,
res: Optional[TimingValue]=None, cap: Optional[TimingValue]=None):
idx = self.node_class_idx(name)
grade = self.speed_grades[self.speed_grade_idx[grade]]
if idx >= len(grade.node_classes):
grade.node_classes += [None for i in range(idx - len(grade.node_classes))]
assert grade.node_classes[idx] is None, f"attempting to set node class {name} to speed grade {grade} twice"
grade.node_classes[idx] = NodeTiming(delay=delay, res=res, cap=cap)

def add_cell_variant(self, speed_grade: str, name: str):
cell = CellTiming(self.strs, name)
self.speed_grades[self.speed_grade_idx[speed_grade]].cell_types.append(cell)
return cell

class Chip:
def __init__(self, uarch: str, name: str, width: int, height: int):
def __init__(self, uarch: str, name: str, width: int, height: int, speed_grades: list=None):
self.strs = StringPool()
self.uarch = uarch
self.name = name
Expand All @@ -386,8 +607,9 @@ def __init__(self, uarch: str, name: str, width: int, height: int):
self.tile_shapes = []
self.tile_shapes_idx = dict()
self.extra_data = None
self.timing = TimingPool(self.strs, speed_grades or [])
def create_tile_type(self, name: str):
tt = TileType(self.strs, self.strs.id(name))
tt = TileType(self.strs, self.timing, self.strs.id(name))
self.tile_type_idx[name] = len(self.tile_types)
self.tile_types.append(tt)
return tt
Expand Down Expand Up @@ -462,7 +684,8 @@ def serialise(self, bba: BBAWriter):
for y, row in enumerate(self.tiles):
for x, tinst in enumerate(row):
tinst.serialise_lists(f"tinst_{x}_{y}", bba)

for i, sg in enumerate(self.timing.speed_grades):
sg.serialise_lists(f"sg{i}", bba)
self.strs.serialise_lists(f"constids", bba)

bba.label(f"tile_types")
Expand All @@ -478,7 +701,9 @@ def serialise(self, bba: BBAWriter):
for y, row in enumerate(self.tiles):
for x, tinst in enumerate(row):
tinst.serialise(f"tinst_{x}_{y}", bba)

bba.label(f"speed_grades")
for i, sg in enumerate(self.timing.speed_grades):
sg.serialise(f"sg{i}", bba)
bba.label(f"constids")
self.strs.serialise(f"constids", bba)

Expand All @@ -500,8 +725,7 @@ def serialise(self, bba: BBAWriter):
bba.u32(0)
bba.u32(0)
# speed grades: not yet used
bba.u32(0)
bba.u32(0)
bba.slice("speed_grades", len(self.timing.speed_grades))
# db-defined constids
bba.ref("constids")
# extra data: not yet used
Expand Down
Loading

0 comments on commit 36a723d

Please sign in to comment.