Skip to content

Commit

Permalink
himbaechel: Generation speedup and improvements
Browse files Browse the repository at this point in the history
Signed-off-by: gatecat <[email protected]>
  • Loading branch information
gatecat committed Oct 29, 2023
1 parent 74d7ebc commit d40c6e8
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 46 deletions.
57 changes: 57 additions & 0 deletions himbaechel/arch.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

NEXTPNR_NAMESPACE_BEGIN

static constexpr int database_version = 2;

static const ChipInfoPOD *get_chip_info(const RelPtr<ChipInfoPOD> *ptr) { return ptr->get(); }

Arch::Arch(ArchArgs args) : args(args)
Expand Down Expand Up @@ -75,6 +77,10 @@ void Arch::load_chipdb(const std::string &path)
// Check consistency of blob
if (chip_info->magic != 0x00ca7ca7)
log_error("chipdb %s does not look like a valid himbächel database!\n", db_path.c_str());
if (chip_info->version != database_version)
log_error(
"chipdb uses db version %d but nextpnr is expecting version %d (did you forget a database rebuild?).\n",
chip_info->version, database_version);
std::string blob_uarch(chip_info->uarch.get());
if (blob_uarch != args.uarch)
log_error("database device uarch '%s' does not match selected device uarch '%s'.\n", blob_uarch.c_str(),
Expand Down Expand Up @@ -102,6 +108,22 @@ void Arch::set_speed_grade(const std::string &speed)
}
}

void Arch::set_package(const std::string &package)
{
if (package.empty())
return;
// Select speed grade
for (const auto &pkg_data : chip_info->packages) {
if (IdString(pkg_data.name) == id(package)) {
package_info = &pkg_data;
break;
}
}
if (!package_info) {
log_error("Package '%s' not found in database.\n", package.c_str());
}
}

void Arch::init_tiles()
{
for (int y = 0; y < chip_info->height; y++) {
Expand Down Expand Up @@ -444,4 +466,39 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
return result;
}

const PadInfoPOD *Arch::get_package_pin(IdString pin) const
{
NPNR_ASSERT(package_info);
for (const auto &pad : package_info->pads) {
if (IdString(pad.package_pin) == pin)
return &pad;
}
return nullptr;
}

const PadInfoPOD *Arch::get_bel_package_pin(BelId bel) const
{
IdStringList bel_name = getBelName(bel);
NPNR_ASSERT(package_info);
for (const auto &pad : package_info->pads) {
if (IdString(pad.tile) == bel_name[0] && IdString(pad.bel) == bel_name[1])
return &pad;
}
return nullptr;
}

BelId Arch::get_package_pin_bel(IdString pin) const
{
auto pin_data = get_package_pin(pin);
if (!pin_data)
return BelId();
return getBelByName(IdStringList::concat(IdString(pin_data->tile), IdString(pin_data->bel)));
}

IdString Arch::get_tile_type(int tile) const
{
auto &tile_data = chip_tile_info(chip_info, tile);
return IdString(tile_data.type_name);
}

NEXTPNR_NAMESPACE_END
7 changes: 7 additions & 0 deletions himbaechel/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ struct Arch : BaseArch<ArchRanges>

void load_chipdb(const std::string &path);
void set_speed_grade(const std::string &speed);
void set_package(const std::string &package);

void late_init();

Expand Down Expand Up @@ -763,6 +764,12 @@ struct Arch : BaseArch<ArchRanges>
std::vector<IdString> tile_name;
dict<IdString, int> tile_name2idx;

// -------------------------------------------------
IdString get_tile_type(int tile) const;
const PadInfoPOD *get_package_pin(IdString pin) const;
const PadInfoPOD *get_bel_package_pin(BelId bel) const;
BelId get_package_pin_bel(IdString pin) const;

// Load capacitance and drive resistance for nodes
// TODO: does this `dict` hurt routing performance too much?
bool fast_pip_delays = false;
Expand Down
2 changes: 2 additions & 0 deletions himbaechel/chipdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ NPNR_PACKED_STRUCT(struct PipDataPOD {
uint32_t type;
uint32_t flags;
int32_t timing_idx;

RelPtr<uint8_t> extra_data;
});

NPNR_PACKED_STRUCT(struct RelTileWireRefPOD {
Expand Down
91 changes: 45 additions & 46 deletions himbaechel/himbaechel_dbgen/chip.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def serialise_lists(self, context: str, bba: BBAWriter):
pin.serialise(f"{context}_pin{i}", bba)
# extra data (optional)
if self.extra_data is not None:
self.extra_data.serialise_lists(f"{context}_extra_data", bba)
bba.label(f"{context}_extra_data")
self.extra_data.serialise(f"{context}_extra_data", bba)
def serialise(self, context: str, bba: BBAWriter):
Expand Down Expand Up @@ -181,15 +182,24 @@ class PipData(BBAStruct):
pip_type: IdString = field(default_factory=IdString)
flags: int = 0
timing_idx: int = -1
extra_data: object = None

def serialise_lists(self, context: str, bba: BBAWriter):
pass
# extra data (optional)
if self.extra_data is not None:
self.extra_data.serialise_lists(f"{context}_extra_data", bba)
bba.label(f"{context}_extra_data")
self.extra_data.serialise(f"{context}_extra_data", bba)
def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.src_wire)
bba.u32(self.dst_wire)
bba.u32(self.pip_type.index)
bba.u32(self.flags)
bba.u32(self.timing_idx)

if self.extra_data is not None:
bba.ref(f"{context}_extra_data")
else:
bba.u32(0)
@dataclass
class TileType(BBAStruct):
strs: StringPool
Expand Down Expand Up @@ -262,6 +272,7 @@ def serialise_lists(self, context: str, bba: BBAWriter):
pip.serialise(f"{context}_pip{i}", bba)
# extra data (optional)
if self.extra_data is not None:
self.extra_data.serialise_lists(f"{context}_extra_data", bba)
bba.label(f"{context}_extra_data")
self.extra_data.serialise(f"{context}_extra_data", bba)
def serialise(self, context: str, bba: BBAWriter):
Expand All @@ -281,41 +292,25 @@ class NodeWire:
y: int
wire: str

# Post deduplication (node shapes merged, relative coords)
@dataclass
class TileWireRef(BBAStruct):
dx: int
dy: int
wire: int

def serialise_lists(self, context: str, bba: BBAWriter):
pass
def serialise(self, context: str, bba: BBAWriter):
bba.u16(self.dx)
bba.u16(self.dy)
bba.u16(self.wire)

@dataclass
class NodeShape(BBAStruct):
wires: list[TileWireRef] = field(default_factory=list)
wires: list[int] = field(default_factory=list)
timing_index: int = -1

def key(self):
m = hashlib.sha1()
for wire in self.wires:
m.update(wire.dx.to_bytes(2, 'little', signed=True))
m.update(wire.dy.to_bytes(2, 'little', signed=True))
m.update(wire.wire.to_bytes(2, 'little'))
m = hashlib.md5()
m.update(struct.pack("h"*len(self.wires), *self.wires))
return m.digest()

def serialise_lists(self, context: str, bba: BBAWriter):
bba.label(f"{context}_wires")
for i, w in enumerate(self.wires):
w.serialise(f"{context}_w{i}", bba)
for w in self.wires:
bba.u16(w)
if len(self.wires) % 2 != 0:
bba.u16(0) # alignment
def serialise(self, context: str, bba: BBAWriter):
bba.slice(f"{context}_wires", len(self.wires))
bba.slice(f"{context}_wires", len(self.wires)//3)
bba.u32(self.timing_index) # timing index (not yet used)

MODE_TILE_WIRE = 0x7000
Expand All @@ -337,23 +332,20 @@ def serialise(self, context: str, bba: BBAWriter):

@dataclass
class TileRoutingShape(BBAStruct):
wire_to_node: list[RelNodeRef] = field(default_factory=list)
wire_to_node: list[int] = field(default_factory=list)
def key(self):
m = hashlib.sha1()
for wire in self.wire_to_node:
m.update(wire.dx_mode.to_bytes(2, 'little', signed=True))
m.update(wire.dy.to_bytes(2, 'little', signed=(wire.dy < 0)))
m.update(wire.wire.to_bytes(2, 'little', signed=True))
m = hashlib.md5()
m.update(struct.pack("h"*len(self.wire_to_node), *self.wire_to_node))
return m.digest()

def serialise_lists(self, context: str, bba: BBAWriter):
bba.label(f"{context}_w2n")
for i, w in enumerate(self.wire_to_node):
w.serialise(f"{context}_w{i}", bba)
for x in self.wire_to_node:
bba.u16(x)
if len(self.wire_to_node) % 2 != 0:
bba.u16(0) # alignment
def serialise(self, context: str, bba: BBAWriter):
bba.slice(f"{context}_w2n", len(self.wire_to_node))
bba.slice(f"{context}_w2n", len(self.wire_to_node)//3)
bba.u32(-1) # timing index

@dataclass
Expand Down Expand Up @@ -701,6 +693,7 @@ def create_tile_type(self, name: str):
return tt
def set_tile_type(self, x: int, y: int, type: str):
self.tiles[y][x].type_idx = self.tile_type_idx[type]
return self.tiles[y][x]
def tile_type_at(self, x: int, y: int):
assert self.tiles[y][x].type_idx is not None, f"tile type at ({x}, {y}) must be set"
return self.tile_types[self.tiles[y][x].type_idx]
Expand All @@ -715,11 +708,12 @@ def add_node(self, wires: list[NodeWire]):
# compute node shape
shape = NodeShape()
for w in wires:
wire_id = w.wire if w.wire is IdString else self.strs.id(w.wire)
shape.wires.append(TileWireRef(
dx=w.x-x0, dy=w.y-y0,
wire=self.tile_type_at(w.x, w.y)._wire2idx[wire_id]
))
if isinstance(w.wire, int):
wire_index = w.wire
else:
wire_id = w.wire if w.wire is IdString else self.strs.id(w.wire)
wire_index = self.tile_type_at(w.x, w.y)._wire2idx[wire_id]
shape.wires += [w.x-x0, w.y-y0, wire_index]
# deduplicate node shapes
key = shape.key()
if key in self.node_shape_idx:
Expand All @@ -731,24 +725,29 @@ def add_node(self, wires: list[NodeWire]):
# update tile wire to node ref
for i, w in enumerate(wires):
inst = self.tiles[w.y][w.x]
wire_idx = shape.wires[i].wire
wire_idx = shape.wires[i*3+2]
# make sure there's actually enough space; first
if wire_idx >= len(inst.shape.wire_to_node):
inst.shape.wire_to_node += [RelNodeRef() for k in range(len(inst.shape.wire_to_node), wire_idx+1)]
while 3*wire_idx >= len(inst.shape.wire_to_node):
inst.shape.wire_to_node += [MODE_TILE_WIRE, 0, 0]
if i == 0:
# root of the node. we don't need to back-reference anything because the node is based here
# so we re-use the structure to store the index of the node shape, instead
assert inst.shape.wire_to_node[wire_idx].dx_mode == MODE_TILE_WIRE, "attempting to add wire to multiple nodes!"
inst.shape.wire_to_node[wire_idx] = RelNodeRef(MODE_IS_ROOT, (shape_idx & 0xFFFF), ((shape_idx >> 16) & 0xFFFF))
assert inst.shape.wire_to_node[3*wire_idx+0] == MODE_TILE_WIRE, "attempting to add wire to multiple nodes!"
inst.shape.wire_to_node[3*wire_idx+0] = MODE_IS_ROOT
inst.shape.wire_to_node[3*wire_idx+1] = (shape_idx & 0xFFFF)
inst.shape.wire_to_node[3*wire_idx+2] = ((shape_idx >> 16) & 0xFFFF)
else:
# back-reference to the root of the node
dx = x0 - w.x
dy = y0 - w.y
assert dx < MODE_TILE_WIRE, "dx range causes overlap with magic values!"
assert inst.shape.wire_to_node[wire_idx].dx_mode == MODE_TILE_WIRE, "attempting to add wire to multiple nodes!"
inst.shape.wire_to_node[wire_idx] = RelNodeRef(dx, dy, shape.wires[0].wire)
assert inst.shape.wire_to_node[3*wire_idx+0] == MODE_TILE_WIRE, "attempting to add wire to multiple nodes!"
inst.shape.wire_to_node[3*wire_idx+0] = dx
inst.shape.wire_to_node[3*wire_idx+1] = dy
inst.shape.wire_to_node[3*wire_idx+2] = shape.wires[0*3+2]

def flatten_tile_shapes(self):
print("Deduplicating tile shapes...")
for row in self.tiles:
for tile in row:
key = tile.shape.key()
Expand Down Expand Up @@ -812,7 +811,7 @@ def serialise(self, bba: BBAWriter):

bba.label("chip_info")
bba.u32(0x00ca7ca7) # magic
bba.u32(1) # version (TODO)
bba.u32(2) # version
bba.u32(self.width)
bba.u32(self.height)

Expand Down

0 comments on commit d40c6e8

Please sign in to comment.