Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
9d4cd25
fix: fix full auto gds flow
KelvinChung2000 Feb 19, 2026
fd6230a
chore: add docs
KelvinChung2000 Mar 17, 2026
ec22fb6
chore: more docs
KelvinChung2000 Mar 17, 2026
c128489
chore: ci
KelvinChung2000 Mar 17, 2026
06eec20
chore: review comment
KelvinChung2000 Mar 17, 2026
cf43834
chore: pre-commit
KelvinChung2000 Apr 14, 2026
3936ac5
WIP: fail upto missing pr boundary
KelvinChung2000 Apr 16, 2026
29275eb
chore: fix test and pre-commit
KelvinChung2000 Apr 17, 2026
16a28c2
chore: more fixes
KelvinChung2000 Apr 17, 2026
4b2157c
chore: un fix a test
KelvinChung2000 Apr 17, 2026
6f0b7e9
chore: improve tile optimisation
KelvinChung2000 Apr 21, 2026
6a83ecb
feat: fixes and performance improvement
KelvinChung2000 Apr 21, 2026
00636ad
chore: another run complete
KelvinChung2000 Apr 22, 2026
fee838a
chore: probably will run
KelvinChung2000 Apr 22, 2026
6b61eb8
chore: test
KelvinChung2000 Apr 28, 2026
1252937
chore: more fixes
KelvinChung2000 Apr 28, 2026
752c1cc
chore: test
KelvinChung2000 Apr 28, 2026
62fa2b6
chore: more clean up
KelvinChung2000 Apr 28, 2026
cd78932
chore: more fix
KelvinChung2000 Apr 28, 2026
095f319
chore: more test fix
KelvinChung2000 Apr 28, 2026
81e06ad
chore: some fixes
KelvinChung2000 May 10, 2026
e985c91
chore: update file path naming
KelvinChung2000 May 12, 2026
74fc58e
chore: make max worker settable
KelvinChung2000 May 12, 2026
ba7e817
Update fabulous/fabulous_settings.py
KelvinChung2000 May 13, 2026
e4ba17d
chore: fix xor miss match
KelvinChung2000 May 14, 2026
dfd4c39
feat: allow starting point to be set
KelvinChung2000 May 18, 2026
3d33a23
chore: fix test
KelvinChung2000 May 18, 2026
c21796c
chore: Fix typo in docstring for conftest.py
KelvinChung2000 May 18, 2026
b43a79f
chore: make the opt ignore default area default
KelvinChung2000 May 19, 2026
de591ce
chore: improve opt mode setting
KelvinChung2000 May 19, 2026
01035fa
feat: IO count based area offset
KelvinChung2000 May 19, 2026
499615e
chore: fix test and pre-commit
KelvinChung2000 May 20, 2026
f30479c
chore: another fix
KelvinChung2000 May 20, 2026
e03d5ca
chore: add step timeout
KelvinChung2000 May 20, 2026
d4e1cc8
fix: fix opt mode regression
KelvinChung2000 May 21, 2026
a5701fb
chore: add test
KelvinChung2000 May 21, 2026
cae572b
chore: clean up round 1
KelvinChung2000 May 21, 2026
62e8233
chore: a lot of test
KelvinChung2000 May 21, 2026
5514396
chore: comment and some stat
KelvinChung2000 May 21, 2026
67d9afc
chore: update flow name
KelvinChung2000 May 22, 2026
56b9711
chore: file name typo
KelvinChung2000 May 22, 2026
14e6007
chore: rename and again and fix "optimization"
KelvinChung2000 May 22, 2026
4766a5c
chore: rename test
KelvinChung2000 May 22, 2026
c93c293
fix: fix fail result leaking
KelvinChung2000 May 26, 2026
81e9ebb
fix: fix an edge case bug
KelvinChung2000 May 28, 2026
cc0fe49
chore: performance improvement
KelvinChung2000 May 28, 2026
c2af613
chore: remove useless feature
KelvinChung2000 May 28, 2026
abef31d
chore: fix test and pre-commit
KelvinChung2000 May 28, 2026
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
2 changes: 1 addition & 1 deletion docs/source/user_guide/building_doc/fabric_definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,7 @@ entity LUT4 is
Exporting configuration bits is a requirement for any primitive or switch matrix that uses configuration bits. The tile configuration bitstream is formed by concatenating first the primitive configuration bits (if primitives are available and use configuration bits) and then the switch matrix configuration bits (again, only if the switch matrix uses configuration bits) into one long tile configuration word.
This is done in the order that the primitives are declared by `BEL` entries in the tile definition. Configuration bitstream vectors are defined in the *downto* direction and the first BEL primitive configuration bits will be placed at the LSB side of the tile bitstream and the configuration switch matrix at the MSB side.

Using the **shift-register configuration mode** will form a tile configuration chain. FABulous only supports one long bit-serial configuration chain. While configuration speed could possibly be boosted by using multiple parallel (and correspondingly shorter) chains, we have not added further optimizations, because shift register configuration is inferior to frame-based configuration mode.
Using the **shift-register configuration mode** will form a tile configuration chain. FABulous only supports one long bit-serial configuration chain. While configuration speed could possibly be boosted by using multiple parallel (and correspondingly shorter) chains, we have not added further optimisations, because shift register configuration is inferior to frame-based configuration mode.

For **frame-based configuration mode**, FABulous will pack those configuration bits into frames. By default, FABulous will start with frame 0 and pack the first `FrameBitsPerRow` bits from the tile configuration bitstream starting with the MSBs of the tile bitstream frame-by-frame until all configuration bits are packed. This may leave some of the `FrameBitsPerRow` x `MaxFramesPerCol` possible configuration bits unused.

Expand Down
34 changes: 17 additions & 17 deletions docs/source/user_guide/building_doc/fabric_gds.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,20 @@ This will generate the tile GDS for you under the tile macro folder (`<project>/

### Command Options

The `gen_tile_macro` command supports an optimization flag:
The `gen_tile_macro` command supports an optimisation flag:

```bash
fabulous> gen_tile_macro <tile_name> --optimise [mode]
```

Where `[mode]` is one of the optimization modes described in the [Tile Size Optimization](#tile-size-optimization) section. If `--optimise` is provided without a mode, `balance` is used by default.
Where `[mode]` is one of the optimisation modes described in the [Tile Size optimisation](#tile-size-optimisation) section. If `--optimise` is provided without a mode, `balance` is used by default.

To generate all tiles at once:

```bash
fabulous> gen_all_tile_macros
fabulous> gen_all_tile_macros --parallel # Run in parallel for faster compilation
fabulous> gen_all_tile_macros --optimise # With optimization (balance mode)
fabulous> gen_all_tile_macros --optimise # With optimisation (balance mode)
```

### Tile Config
Expand All @@ -92,7 +92,7 @@ The per tile `gds_config.yaml` is particularly useful and important as you can s

### Pin Config

During the generation process there will be an extra file generated under the `macro` folder, which is the `io_pin_order.yaml`. This file controls the placement of the IO pins along the tile. This is auto-populated to make sure all the pins of a tile align with the adjacent tiles. But one can modify it for whatever means, such as optimization. The following is an example of the IO config file:
During the generation process there will be an extra file generated under the `macro` folder, which is the `io_pin_order.yaml`. This file controls the placement of the IO pins along the tile. This is auto-populated to make sure all the pins of a tile align with the adjacent tiles. But one can modify it for whatever means, such as optimisation. The following is an example of the IO config file:

```yaml
X0Y0:
Expand Down Expand Up @@ -173,21 +173,21 @@ Same as tile implementation, there is a `gds_config.yaml` file under the `Fabric

## Full Automated Flow

For a fully automated flow that handles tile size optimization and fabric stitching, use:
For a fully automated flow that handles tile size optimisation and fabric stitching, use:

```bash
fabulous> run_FABulous_eFPGA_macro
```

:::{note}
The fully automated flow can take significantly longer than manual tile compilation, as it performs design space exploration by compiling all tiles with multiple optimization modes in parallel before running NLP optimization. For large fabrics with many unique tiles, expect longer runtimes.
The fully automated flow can take significantly longer than manual tile compilation, as it performs design space exploration by compiling all tiles with multiple optimisation modes in parallel before running NLP optimisation. For large fabrics with many unique tiles, expect longer runtimes.
:::

This command performs the following steps automatically:

1. **Design Space Exploration**: Compiles all tiles with three optimization modes (`balance`, `find_min_width`, `find_min_height`) in parallel to explore possible tile dimensions.
1. **Design Space Exploration**: Compiles all tiles with three optimisation modes (`balance`, `find_min_width`, `find_min_height`) in parallel to explore possible tile dimensions.

2. **NLP Optimization**: Uses Non-Linear Programming (via pymoo) to find optimal tile dimensions that minimize total fabric area while satisfying:
2. **NLP optimisation**: Uses Non-Linear Programming (via pymoo) to find optimal tile dimensions that minimize total fabric area while satisfying:
- Minimum area constraints for each tile
- Row height consistency (all tiles in a row must have the same height)
- Column width consistency (all tiles in a column must have the same width)
Expand All @@ -197,23 +197,23 @@ This command performs the following steps automatically:

4. **Fabric Stitching**: Assembles all tiles into the final fabric layout.

(tile-size-optimization)=
(tile-size-optimisation)=

## Tile Size Optimization
## Tile Size Optimisation

The GDS flow includes an iterative optimization process to find the minimum viable tile dimensions. This is controlled by the `FABULOUS_OPT_MODE` variable.
The GDS flow includes an iterative optimisation process to find the minimum viable tile dimensions. This is controlled by the `FABULOUS_OPT_MODE` variable.

### Optimization Modes
### Optimisation Modes

| Mode | Description | Use Case |
|------|-------------|----------|
| `balance` | Alternates between increasing width and height to find minimal area | **Recommended** - Best for most tiles |
| `find_min_width` | Increases width iteratively while keeping height fixed | When height is constrained |
| `find_min_height` | Increases height iteratively while keeping width fixed | When width is constrained |
| `large` | Increases both dimensions together | Quick compilation, larger area |
| `no_opt` | No optimization, uses provided `DIE_AREA` directly | Manual control, requires `DIE_AREA` to be set |
| `no_opt` | No optimisation, uses provided `DIE_AREA` directly | Manual control, requires `DIE_AREA` to be set |

### How Optimization Works
### How Optimisation Works

1. The flow starts with an initial die area (either provided or calculated from instance area)
2. It runs through placement and routing
Expand All @@ -239,9 +239,9 @@ After successful compilation, the output is organized as follows:
├── Tile/
│ └── <tile_name>/
│ └── macro/
│ ├── balance/ # Output from balance optimization
│ ├── find_min_width/ # Output from width optimization
│ ├── find_min_height/ # Output from height optimization
│ ├── balance/ # Output from balance optimisation
│ ├── find_min_width/ # Output from width optimisation
│ ├── find_min_height/ # Output from height optimisation
│ └── final_views/ # Final compiled output
│ ├── gds/ # GDSII files
│ ├── lef/ # LEF macro files
Expand Down
67 changes: 26 additions & 41 deletions fabulous/fabric_definition/supertile.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,66 +128,51 @@ def get_min_die_area(
self,
x_pitch: Decimal,
y_pitch: Decimal,
x_pin_thickness_mult: Decimal,
y_pin_thickness_mult: Decimal,
x_spacing: Decimal,
y_spacing: Decimal,
x_pin_thickness_mult: Decimal = Decimal(1),
y_pin_thickness_mult: Decimal = Decimal(1),
edge_offset: int = 2,
) -> tuple[Decimal, Decimal]:
"""Calculate minimum SuperTile dimensions based on IO pin density.
"""Calculate minimum SuperTile dimensions based on IO pin track requirements.

For this supertile, aggregates IO pins from all constituent tiles
that appear on the outer edges and calculates the minimum physical
width and height required.
Takes the maximum per-side IO pin count across all constituent subtiles
as a conservative upper bound, then derives the minimum physical width
and height required.

See ``Tile.get_min_die_area`` for the track-based derivation.

Parameters
----------
x_pitch : Decimal
Horizontal pitch between tracks (DBU).
Vertical-layer track pitch (for north/south pins).
y_pitch : Decimal
Vertical pitch between tracks (DBU).
Horizontal-layer track pitch (for east/west pins).
x_pin_thickness_mult : Decimal
Pin thickness multiplier in the horizontal direction.
Number of tracks each north/south pin spans, by default 1.
y_pin_thickness_mult : Decimal
Pin thickness multiplier in the vertical direction.
x_spacing : Decimal
Pin spacing in the horizontal direction (DBU).
y_spacing : Decimal
Pin spacing in the vertical direction (DBU).
Number of tracks each east/west pin spans, by default 1.
edge_offset : int, optional
Reserved tracks at tile edge, by default 2.

Returns
-------
tuple[Decimal, Decimal]
(min_width, min_height) where:
- min_width: minimum width needed for north/south edge IO pins
- min_height: minimum height needed for west/east edge IO pins

Notes
-----
For supertiles, we aggregate IO pins from all constituent tiles
that appear on the outer edges of the supertile to get conservative
estimates for minimum dimensions.
(min_width, min_height)
"""
max_north = 0
max_south = 0
max_west = 0
max_east = 0

for subtile in self.tiles:
north_ports = subtile.get_port_count(Side.NORTH)
south_ports = subtile.get_port_count(Side.SOUTH)
west_ports = subtile.get_port_count(Side.WEST)
east_ports = subtile.get_port_count(Side.EAST)

max_north = max(max_north, north_ports)
max_south = max(max_south, south_ports)
max_west = max(max_west, west_ports)
max_east = max(max_east, east_ports)

min_width_io = Decimal(max(max_north, max_south)) * (
x_pitch * x_pin_thickness_mult + x_spacing
)
min_height_io = Decimal(max(max_west, max_east)) * (
y_pitch * y_pin_thickness_mult + y_spacing
)
max_north = max(max_north, subtile.get_port_count(Side.NORTH))
max_south = max(max_south, subtile.get_port_count(Side.SOUTH))
max_west = max(max_west, subtile.get_port_count(Side.WEST))
max_east = max(max_east, subtile.get_port_count(Side.EAST))

x_io_count = Decimal(max(max_north, max_south))
min_width_io = (x_io_count * x_pin_thickness_mult + edge_offset) * x_pitch

y_io_count = Decimal(max(max_west, max_east))
min_height_io = (y_io_count * y_pin_thickness_mult + edge_offset) * y_pitch

return min_width_io, min_height_io
92 changes: 39 additions & 53 deletions fabulous/fabric_definition/tile.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Tile class definition for FPGA fabric representation."""

import itertools
from dataclasses import dataclass, field
from decimal import Decimal
from pathlib import Path
Expand Down Expand Up @@ -313,7 +312,7 @@ def globalConfigBits(self) -> int:
return ret

def get_port_count(self, side: Side) -> int:
"""Count total number of expanded ports on a given side of the tile.
"""Count total number of expanded physical pins on a given side of the tile.

Parameters
----------
Expand All @@ -325,87 +324,74 @@ def get_port_count(self, side: Side) -> int:
int
Total number of expanded ports on the given side.
"""
ports = [p for p in self.portsInfo if p.sideOfTile == side and p.name != "NULL"]
return len(
list(
itertools.chain.from_iterable(
[
list(itertools.chain.from_iterable(p.expandPortInfo("all")))
for p in ports
]
)
)
)
total = 0
for p in self.portsInfo:
if p.sideOfTile != side or p.name == "NULL":
continue
inputs, outputs = p.expandPortInfo("all")
if p.name == p.sourceName:
total += len(inputs)
elif p.name == p.destinationName:
total += len(outputs)

return total

def get_min_die_area(
self,
x_pitch: Decimal,
y_pitch: Decimal,
x_pin_thickness_mult: Decimal,
y_pin_thickness_mult: Decimal,
x_spacing: Decimal,
y_spacing: Decimal,
x_pin_thickness_mult: Decimal = Decimal(1),
y_pin_thickness_mult: Decimal = Decimal(1),
frame_data_width: int = 32,
frame_strobe_width: int = 20,
edge_offset: int = 2,
) -> tuple[Decimal, Decimal]:
"""Calculate minimum tile dimensions based on IO pin density.
"""Calculate minimum tile dimensions based on IO pin track requirements.

The IO pin placer distributes pins across available tracks on each
tile edge. Each pin occupies ``thickness_mult`` consecutive tracks,
and ``edge_offset`` tracks are reserved at the start of the tile
(see ``tile_io_place.allocate_tracks``).

The minimum number of tracks on a side is therefore::

required_tracks = pin_count * thickness_mult + edge_offset

For this tile, calculates the minimum physical width and height
required to accommodate all IO pins at the PDK's track pitch.
And the minimum physical dimension is::

min_dim = required_tracks * pitch

Parameters
----------
x_pitch : Decimal
Horizontal pitch between tracks (DBU).
Vertical-layer track pitch (for north/south pins).
y_pitch : Decimal
Vertical pitch between tracks (DBU).
Horizontal-layer track pitch (for east/west pins).
x_pin_thickness_mult : Decimal
Pin thickness multiplier in the horizontal direction.
Number of tracks each north/south pin spans, by default 1.
y_pin_thickness_mult : Decimal
Pin thickness multiplier in the vertical direction.
x_spacing : Decimal
Pin spacing in the horizontal direction (DBU).
y_spacing : Decimal
Pin spacing in the vertical direction (DBU).
Number of tracks each east/west pin spans, by default 1.
frame_data_width : int, optional
Frame data width, by default 32.
frame_strobe_width : int, optional
Frame strobe width, by default 20.
edge_offset : int, optional
Reserved tracks at tile edge, by default 2.

Returns
-------
tuple[Decimal, Decimal]
(min_width, min_height) where:
- min_width: minimum width needed for north/south edge IO pins
- min_height: minimum height needed for west/east edge IO pins

Notes
-----
The minimum dimensions are calculated as:
- min_width = max(north_pins, south_pins) * x_pitch
- min_height = max(west_pins, east_pins) * y_pitch

These constraints prevent the NLP solver from suggesting dimensions
that are physically impossible due to IO pin spacing requirements.
(min_width, min_height)
"""
# Count ports on each physical side
north_ports = self.get_port_count(Side.NORTH)
south_ports = self.get_port_count(Side.SOUTH)
west_ports = self.get_port_count(Side.WEST)
east_ports = self.get_port_count(Side.EAST)

# Min width constrained by north/south edges
x_io_count = Decimal(max(north_ports, south_ports) + frame_strobe_width)
min_width_io = (
x_io_count * (x_pitch * x_pin_thickness_mult)
+ x_spacing * x_io_count
+ 2 * x_spacing
)
# Min height constrained by west/east edges
min_width_io = (x_io_count * x_pin_thickness_mult + edge_offset) * x_pitch

y_io_count = Decimal(max(west_ports, east_ports) + frame_data_width)
min_height_io = (
y_io_count * (y_pitch * y_pin_thickness_mult)
+ y_spacing * y_io_count
+ 2 * y_spacing
)
min_height_io = (y_io_count * y_pin_thickness_mult + edge_offset) * y_pitch

return min_width_io, min_height_io
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def __init__(
name=self.fabric.name,
design_dir=final_design_dir,
pdk=pdk,
pdk_root=str(pdk_root.resolve()),
pdk_root=str(pdk_root),
)

def _compute_row_and_column_sizes(
Expand Down
Loading
Loading