himbaechel: Generation speedup and improvements

Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
gatecat 2023-10-28 17:22:19 +02:00 committed by myrtle
parent 74d7ebc71f
commit d40c6e850d
4 changed files with 111 additions and 46 deletions

View File

@ -32,6 +32,8 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
static constexpr int database_version = 2;
static const ChipInfoPOD *get_chip_info(const RelPtr<ChipInfoPOD> *ptr) { return ptr->get(); } static const ChipInfoPOD *get_chip_info(const RelPtr<ChipInfoPOD> *ptr) { return ptr->get(); }
Arch::Arch(ArchArgs args) : args(args) Arch::Arch(ArchArgs args) : args(args)
@ -75,6 +77,10 @@ void Arch::load_chipdb(const std::string &path)
// Check consistency of blob // Check consistency of blob
if (chip_info->magic != 0x00ca7ca7) if (chip_info->magic != 0x00ca7ca7)
log_error("chipdb %s does not look like a valid himbächel database!\n", db_path.c_str()); 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()); std::string blob_uarch(chip_info->uarch.get());
if (blob_uarch != args.uarch) if (blob_uarch != args.uarch)
log_error("database device uarch '%s' does not match selected device uarch '%s'.\n", blob_uarch.c_str(), log_error("database device uarch '%s' does not match selected device uarch '%s'.\n", blob_uarch.c_str(),
@ -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() void Arch::init_tiles()
{ {
for (int y = 0; y < chip_info->height; y++) { for (int y = 0; y < chip_info->height; y++) {
@ -444,4 +466,39 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
return result; 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 NEXTPNR_NAMESPACE_END

View File

@ -422,6 +422,7 @@ struct Arch : BaseArch<ArchRanges>
void load_chipdb(const std::string &path); void load_chipdb(const std::string &path);
void set_speed_grade(const std::string &speed); void set_speed_grade(const std::string &speed);
void set_package(const std::string &package);
void late_init(); void late_init();
@ -763,6 +764,12 @@ struct Arch : BaseArch<ArchRanges>
std::vector<IdString> tile_name; std::vector<IdString> tile_name;
dict<IdString, int> tile_name2idx; 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 // Load capacitance and drive resistance for nodes
// TODO: does this `dict` hurt routing performance too much? // TODO: does this `dict` hurt routing performance too much?
bool fast_pip_delays = false; bool fast_pip_delays = false;

View File

@ -76,6 +76,8 @@ NPNR_PACKED_STRUCT(struct PipDataPOD {
uint32_t type; uint32_t type;
uint32_t flags; uint32_t flags;
int32_t timing_idx; int32_t timing_idx;
RelPtr<uint8_t> extra_data;
}); });
NPNR_PACKED_STRUCT(struct RelTileWireRefPOD { NPNR_PACKED_STRUCT(struct RelTileWireRefPOD {

View File

@ -112,6 +112,7 @@ class BelData(BBAStruct):
pin.serialise(f"{context}_pin{i}", bba) pin.serialise(f"{context}_pin{i}", bba)
# extra data (optional) # extra data (optional)
if self.extra_data is not None: if self.extra_data is not None:
self.extra_data.serialise_lists(f"{context}_extra_data", bba)
bba.label(f"{context}_extra_data") bba.label(f"{context}_extra_data")
self.extra_data.serialise(f"{context}_extra_data", bba) self.extra_data.serialise(f"{context}_extra_data", bba)
def serialise(self, context: str, bba: BBAWriter): def serialise(self, context: str, bba: BBAWriter):
@ -181,15 +182,24 @@ class PipData(BBAStruct):
pip_type: IdString = field(default_factory=IdString) pip_type: IdString = field(default_factory=IdString)
flags: int = 0 flags: int = 0
timing_idx: int = -1 timing_idx: int = -1
extra_data: object = None
def serialise_lists(self, context: str, bba: BBAWriter): 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): def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.src_wire) bba.u32(self.src_wire)
bba.u32(self.dst_wire) bba.u32(self.dst_wire)
bba.u32(self.pip_type.index) bba.u32(self.pip_type.index)
bba.u32(self.flags) bba.u32(self.flags)
bba.u32(self.timing_idx) bba.u32(self.timing_idx)
if self.extra_data is not None:
bba.ref(f"{context}_extra_data")
else:
bba.u32(0)
@dataclass @dataclass
class TileType(BBAStruct): class TileType(BBAStruct):
strs: StringPool strs: StringPool
@ -262,6 +272,7 @@ class TileType(BBAStruct):
pip.serialise(f"{context}_pip{i}", bba) pip.serialise(f"{context}_pip{i}", bba)
# extra data (optional) # extra data (optional)
if self.extra_data is not None: if self.extra_data is not None:
self.extra_data.serialise_lists(f"{context}_extra_data", bba)
bba.label(f"{context}_extra_data") bba.label(f"{context}_extra_data")
self.extra_data.serialise(f"{context}_extra_data", bba) self.extra_data.serialise(f"{context}_extra_data", bba)
def serialise(self, context: str, bba: BBAWriter): def serialise(self, context: str, bba: BBAWriter):
@ -281,41 +292,25 @@ class NodeWire:
y: int y: int
wire: str 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 @dataclass
class NodeShape(BBAStruct): class NodeShape(BBAStruct):
wires: list[TileWireRef] = field(default_factory=list) wires: list[int] = field(default_factory=list)
timing_index: int = -1 timing_index: int = -1
def key(self): def key(self):
m = hashlib.sha1() m = hashlib.md5()
for wire in self.wires: m.update(struct.pack("h"*len(self.wires), *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'))
return m.digest() return m.digest()
def serialise_lists(self, context: str, bba: BBAWriter): def serialise_lists(self, context: str, bba: BBAWriter):
bba.label(f"{context}_wires") bba.label(f"{context}_wires")
for i, w in enumerate(self.wires): for w in self.wires:
w.serialise(f"{context}_w{i}", bba) bba.u16(w)
if len(self.wires) % 2 != 0: if len(self.wires) % 2 != 0:
bba.u16(0) # alignment bba.u16(0) # alignment
def serialise(self, context: str, bba: BBAWriter): 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) bba.u32(self.timing_index) # timing index (not yet used)
MODE_TILE_WIRE = 0x7000 MODE_TILE_WIRE = 0x7000
@ -337,23 +332,20 @@ class RelNodeRef(BBAStruct):
@dataclass @dataclass
class TileRoutingShape(BBAStruct): class TileRoutingShape(BBAStruct):
wire_to_node: list[RelNodeRef] = field(default_factory=list) wire_to_node: list[int] = field(default_factory=list)
def key(self): def key(self):
m = hashlib.sha1() m = hashlib.md5()
for wire in self.wire_to_node: m.update(struct.pack("h"*len(self.wire_to_node), *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))
return m.digest() return m.digest()
def serialise_lists(self, context: str, bba: BBAWriter): def serialise_lists(self, context: str, bba: BBAWriter):
bba.label(f"{context}_w2n") bba.label(f"{context}_w2n")
for i, w in enumerate(self.wire_to_node): for x in self.wire_to_node:
w.serialise(f"{context}_w{i}", bba) bba.u16(x)
if len(self.wire_to_node) % 2 != 0: if len(self.wire_to_node) % 2 != 0:
bba.u16(0) # alignment bba.u16(0) # alignment
def serialise(self, context: str, bba: BBAWriter): 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 bba.u32(-1) # timing index
@dataclass @dataclass
@ -701,6 +693,7 @@ class Chip:
return tt return tt
def set_tile_type(self, x: int, y: int, type: str): def set_tile_type(self, x: int, y: int, type: str):
self.tiles[y][x].type_idx = self.tile_type_idx[type] 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): 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" 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] return self.tile_types[self.tiles[y][x].type_idx]
@ -715,11 +708,12 @@ class Chip:
# compute node shape # compute node shape
shape = NodeShape() shape = NodeShape()
for w in wires: for w in wires:
wire_id = w.wire if w.wire is IdString else self.strs.id(w.wire) if isinstance(w.wire, int):
shape.wires.append(TileWireRef( wire_index = w.wire
dx=w.x-x0, dy=w.y-y0, else:
wire=self.tile_type_at(w.x, w.y)._wire2idx[wire_id] 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 # deduplicate node shapes
key = shape.key() key = shape.key()
if key in self.node_shape_idx: if key in self.node_shape_idx:
@ -731,24 +725,29 @@ class Chip:
# update tile wire to node ref # update tile wire to node ref
for i, w in enumerate(wires): for i, w in enumerate(wires):
inst = self.tiles[w.y][w.x] 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 # make sure there's actually enough space; first
if wire_idx >= len(inst.shape.wire_to_node): while 3*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)] inst.shape.wire_to_node += [MODE_TILE_WIRE, 0, 0]
if i == 0: if i == 0:
# root of the node. we don't need to back-reference anything because the node is based here # 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 # 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!" 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[wire_idx] = RelNodeRef(MODE_IS_ROOT, (shape_idx & 0xFFFF), ((shape_idx >> 16) & 0xFFFF)) 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: else:
# back-reference to the root of the node # back-reference to the root of the node
dx = x0 - w.x dx = x0 - w.x
dy = y0 - w.y dy = y0 - w.y
assert dx < MODE_TILE_WIRE, "dx range causes overlap with magic values!" 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!" 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[wire_idx] = RelNodeRef(dx, dy, shape.wires[0].wire) 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): def flatten_tile_shapes(self):
print("Deduplicating tile shapes...")
for row in self.tiles: for row in self.tiles:
for tile in row: for tile in row:
key = tile.shape.key() key = tile.shape.key()
@ -812,7 +811,7 @@ class Chip:
bba.label("chip_info") bba.label("chip_info")
bba.u32(0x00ca7ca7) # magic bba.u32(0x00ca7ca7) # magic
bba.u32(1) # version (TODO) bba.u32(2) # version
bba.u32(self.width) bba.u32(self.width)
bba.u32(self.height) bba.u32(self.height)