Skip to content

Commit 78da9d9

Browse files
feat: improve tapeout performance
1 parent f6c9a5f commit 78da9d9

7 files changed

Lines changed: 690 additions & 361 deletions

File tree

ip/bin/aegis_genip.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,49 @@ Future<void> main(List<String> arguments) async {
174174
serdesCount: serdesCount,
175175
clockTileCount: clockTiles,
176176
);
177+
// Top-level assembly script (reads pre-synthesized tile macros)
177178
File(
178179
'$outputDir/${fpga.name}-yosys.tcl',
179180
).writeAsStringSync(yosysEmitter.generate());
181+
// Per-tile synthesis scripts (one per unique tile type)
182+
for (final mod in YosysTclEmitter.tileModules) {
183+
File(
184+
'$outputDir/${fpga.name}-yosys-$mod.tcl',
185+
).writeAsStringSync(yosysEmitter.generateTileSynth(mod));
186+
}
187+
188+
// Generate tile blackbox stubs for top-level synthesis
189+
// Extract module port declarations from the generated SV
190+
final svContent = File('$outputDir/${fpga.name}.sv').readAsStringSync();
191+
final stubs = StringBuffer();
192+
stubs.writeln(
193+
'// Auto-generated tile blackbox stubs for hierarchical synthesis',
194+
);
195+
stubs.writeln(
196+
'// These are empty modules with matching ports so the top-level',
197+
);
198+
stubs.writeln(
199+
'// synthesis can elaborate without processing tile internals.',
200+
);
201+
stubs.writeln();
202+
for (final mod in YosysTclEmitter.tileModules) {
203+
// Find the module declaration in the SV
204+
final modStart = svContent.indexOf('module $mod (');
205+
if (modStart == -1) continue;
206+
207+
// Extract from "module X (" to the closing ");"
208+
final portEnd = svContent.indexOf(');', modStart);
209+
if (portEnd == -1) continue;
210+
211+
final decl = svContent.substring(modStart, portEnd + 2);
212+
stubs.writeln('(* blackbox *)');
213+
stubs.writeln(decl);
214+
stubs.writeln('endmodule');
215+
stubs.writeln();
216+
}
217+
File(
218+
'$outputDir/${fpga.name}_tile_stubs.v',
219+
).writeAsStringSync(stubs.toString());
180220

181221
final techmapEmitter = YosysTechmapEmitter(
182222
deviceName: fpga.name,
@@ -214,16 +254,33 @@ Future<void> main(List<String> arguments) async {
214254
'$outputDir/${fpga.name}-openroad.tcl',
215255
).writeAsStringSync(openroadEmitter.generate());
216256

257+
// Per-tile OpenROAD PnR scripts (tile macro hardening)
258+
final tileOpenroadEmitter = OpenroadTileTclEmitter(
259+
deviceName: fpga.name,
260+
tracks: tracks,
261+
);
262+
for (final mod in YosysTclEmitter.tileModules) {
263+
File(
264+
'$outputDir/${fpga.name}-openroad-$mod.tcl',
265+
).writeAsStringSync(tileOpenroadEmitter.generateTilePnr(mod));
266+
}
267+
217268
print('Generated:');
218269
print(' $outputDir/${fpga.name}.sv');
219270
print(' $outputDir/${fpga.name}.json');
220271
print(' $outputDir/${fpga.name}-xschem.tcl');
221272
print(' $outputDir/${fpga.name}-xschem.sch');
222273
print(' $outputDir/${fpga.name}-yosys.tcl');
274+
for (final mod in YosysTclEmitter.tileModules) {
275+
print(' $outputDir/${fpga.name}-yosys-$mod.tcl');
276+
}
223277
print(' $outputDir/${fpga.name}_cells.v');
224278
print(' $outputDir/${fpga.name}_techmap.v');
225279
print(' $outputDir/${fpga.name}-synth-aegis.tcl');
226280
print(' $outputDir/${fpga.name}-openroad.tcl');
281+
for (final mod in YosysTclEmitter.tileModules) {
282+
print(' $outputDir/${fpga.name}-openroad-$mod.tcl');
283+
}
227284
} on FormatException catch (e) {
228285
print(e.message);
229286
print('');
Lines changed: 74 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:rohd/rohd.dart';
22
import 'package:rohd_hcl/rohd_hcl.dart';
33

4+
/// Streams configuration bits into the FPGA fabric config chain.
45
class FabricConfigLoader extends Module {
56
Logic get clk => input('clk');
67
Logic get start => input('start');
@@ -37,92 +38,81 @@ class FabricConfigLoader extends Module {
3738
final wordWidth = readPort.data.width;
3839
final wordsNeeded = (totalBits + wordWidth - 1) ~/ wordWidth;
3940

40-
// Use rohd_hcl Deserializer to collect words from memory into a
41-
// flat LogicArray, then a Serializer to shift them out bit-by-bit.
4241
final active = Logic(name: 'active');
4342
final wordAddr = Logic(width: readPort.addr.width, name: 'wordAddr');
44-
final wordCount = Logic(width: wordsNeeded.bitLength, name: 'wordCount');
45-
final fetchDone = Logic(name: 'fetchDone');
46-
47-
// --- Word fetch FSM: reads words from memory sequentially ---
48-
Sequential(clk, [
49-
If(
50-
start,
51-
then: [
52-
active < Const(1),
53-
wordAddr < Const(0, width: wordAddr.width),
54-
wordCount < Const(0, width: wordCount.width),
55-
fetchDone < Const(0),
56-
],
57-
orElse: [
58-
If(
59-
active & ~fetchDone,
60-
then: [
61-
wordCount < wordCount + 1,
62-
wordAddr < wordAddr + 1,
63-
If(
64-
wordCount.eq(Const(wordsNeeded - 1, width: wordCount.width)),
65-
then: [fetchDone < Const(1)],
66-
),
67-
],
68-
),
69-
],
70-
),
71-
]);
72-
73-
readPort.en <= active & ~fetchDone;
74-
readPort.addr <= wordAddr;
75-
76-
// --- Deserializer: collects words into a wide register ---
77-
final deser = Deserializer(
78-
readPort.data,
79-
wordsNeeded,
80-
clk: clk,
81-
reset: start,
82-
enable: active & ~fetchDone,
43+
final wordBuf = Logic(width: wordWidth, name: 'wordBuf');
44+
final bitIdx = Logic(width: wordWidth.bitLength, name: 'bitIdx');
45+
final totalShifted = Logic(
46+
width: totalBits.bitLength,
47+
name: 'totalShifted',
8348
);
84-
85-
// --- Serializer: shifts the collected words out bit-by-bit ---
86-
// We need to serialize the deserialized array one bit at a time.
87-
// Use a simple bit counter + shift approach on the deserialized output.
88-
final bitCounter = Logic(width: totalBits.bitLength, name: 'bitCounter');
8949
final shifting = Logic(name: 'shifting');
90-
final shiftDone = Logic(name: 'shiftDone');
91-
92-
// Flatten the deserialized array into a single wide bus
93-
final flatBits = Logic(width: wordsNeeded * wordWidth, name: 'flatBits');
94-
flatBits <=
95-
deser.deserialized.elements
96-
.map((e) => e)
97-
.toList()
98-
.reversed
99-
.toList()
100-
.swizzle();
101-
102-
// Latch the flat bits when deserialization completes
103-
final latchedBits = Logic(
104-
width: wordsNeeded * wordWidth,
105-
name: 'latchedBits',
106-
);
50+
final wordReady = Logic(name: 'wordReady');
51+
final allDone = Logic(name: 'allDone');
10752

10853
Sequential(
10954
clk,
11055
[
11156
If(
112-
deser.done & active,
57+
start,
11358
then: [
114-
latchedBits < flatBits,
115-
shifting < Const(1),
116-
bitCounter < Const(0, width: bitCounter.width),
59+
active < Const(1),
60+
wordAddr < Const(0, width: wordAddr.width),
61+
bitIdx < Const(0, width: bitIdx.width),
62+
totalShifted < Const(0, width: totalShifted.width),
63+
shifting < Const(0),
64+
wordReady < Const(0),
65+
allDone < Const(0),
11766
],
11867
orElse: [
11968
If(
120-
shifting,
69+
active & ~allDone,
12170
then: [
122-
bitCounter < bitCounter + 1,
12371
If(
124-
bitCounter.eq(Const(totalBits - 1, width: bitCounter.width)),
125-
then: [shifting < Const(0), shiftDone < Const(1)],
72+
~shifting & ~wordReady,
73+
then: [
74+
wordBuf < readPort.data,
75+
wordReady < Const(1),
76+
bitIdx < Const(0, width: bitIdx.width),
77+
],
78+
orElse: [
79+
If(
80+
wordReady & ~shifting,
81+
then: [shifting < Const(1)],
82+
orElse: [
83+
If(
84+
shifting,
85+
then: [
86+
bitIdx < bitIdx + 1,
87+
totalShifted < totalShifted + 1,
88+
If(
89+
totalShifted.eq(
90+
Const(totalBits - 1, width: totalShifted.width),
91+
),
92+
then: [
93+
// All bits shifted
94+
allDone < Const(1),
95+
shifting < Const(0),
96+
],
97+
orElse: [
98+
If(
99+
bitIdx.eq(
100+
Const(wordWidth - 1, width: bitIdx.width),
101+
),
102+
then: [
103+
// Word exhausted, fetch next
104+
shifting < Const(0),
105+
wordReady < Const(0),
106+
wordAddr < wordAddr + 1,
107+
],
108+
),
109+
],
110+
),
111+
],
112+
),
113+
],
114+
),
115+
],
126116
),
127117
],
128118
),
@@ -131,15 +121,22 @@ class FabricConfigLoader extends Module {
131121
],
132122
reset: start,
133123
resetValues: {
124+
active: Const(0),
134125
shifting: Const(0),
135-
shiftDone: Const(0),
136-
bitCounter: Const(0, width: bitCounter.width),
137-
latchedBits: Const(0, width: latchedBits.width),
126+
wordReady: Const(0),
127+
allDone: Const(0),
128+
wordAddr: Const(0, width: wordAddr.width),
129+
wordBuf: Const(0, width: wordWidth),
130+
bitIdx: Const(0, width: bitIdx.width),
131+
totalShifted: Const(0, width: totalShifted.width),
138132
},
139133
);
140134

141-
cfgIn <= mux(shifting, latchedBits[bitCounter], Const(0));
142-
cfgLoad <= shiftDone;
143-
done <= shiftDone & ~active;
135+
readPort.en <= active & ~allDone & ~shifting & ~wordReady;
136+
readPort.addr <= wordAddr;
137+
138+
cfgIn <= mux(shifting, wordBuf[bitIdx], Const(0));
139+
cfgLoad <= allDone;
140+
done <= allDone;
144141
}
145142
}

ip/lib/src/openroad.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export 'openroad/tcl_emitter.dart';
2+
export 'openroad/tile_tcl_emitter.dart';

0 commit comments

Comments
 (0)