Skip to content

Commit d88d2f0

Browse files
committed
format
1 parent 12bb1af commit d88d2f0

File tree

3 files changed

+159
-43
lines changed

3 files changed

+159
-43
lines changed

lib/CAPI/Translation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ MlirOperation aieTranslateBinaryToTxn(MlirContext ctx, MlirStringRef binary) {
9595
}
9696

9797
MlirOperation aieTranslateBinaryToControlPackets(MlirContext ctx,
98-
MlirStringRef binary) {
98+
MlirStringRef binary) {
9999
std::vector<uint8_t> binaryData(binary.data, binary.data + binary.length);
100100
auto mod = convertControlPacketBinaryToMLIR(unwrap(ctx), binaryData);
101101
if (!mod)

lib/Conversion/AIEToConfiguration/AIEToConfiguration.cpp

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,10 @@ struct TxnLoadPdiHeader {
103103
// A ControlPacketOperation encapsulates a parsed control packet including
104104
// stream header, control packet header, and optional data payload.
105105
struct ControlPacketOperation {
106-
uint32_t streamHeader; // word 0: parity + pkt_type + pkt_id
107-
uint32_t packetHeader; // word 1: parity + stream_id + opcode + beats + addr
108-
std::vector<uint32_t> data; // payload words (if opcode is write/blockwrite)
109-
106+
uint32_t streamHeader; // word 0: parity + pkt_type + pkt_id
107+
uint32_t packetHeader; // word 1: parity + stream_id + opcode + beats + addr
108+
std::vector<uint32_t> data; // payload words (if opcode is write/blockwrite)
109+
110110
// Decoded fields
111111
uint8_t pktType;
112112
uint8_t pktId;
@@ -138,90 +138,92 @@ static bool checkParity(uint32_t word) {
138138
static std::optional<std::vector<ControlPacketOperation>>
139139
parseControlPacket(const std::vector<uint8_t> &data) {
140140
std::vector<ControlPacketOperation> ops;
141-
141+
142142
size_t i = 0;
143-
143+
144144
auto requireBytes = [&](size_t offset, size_t length) -> bool {
145145
if (offset + length > data.size()) {
146146
llvm::errs() << "Control packet binary truncated\n";
147147
return false;
148148
}
149149
return true;
150150
};
151-
151+
152152
auto read32 = [&](size_t offset) -> uint32_t {
153153
uint32_t value;
154154
std::memcpy(&value, data.data() + offset, sizeof(uint32_t));
155155
return value;
156156
};
157-
158-
while (i + 8 <= data.size()) { // Need at least 2 words (stream hdr + pkt hdr)
157+
158+
while (i + 8 <= data.size()) { // Need at least 2 words (stream hdr + pkt hdr)
159159
ControlPacketOperation op;
160-
160+
161161
// Read stream header (word 0)
162162
op.streamHeader = read32(i);
163-
163+
164164
// Read control packet header (word 1)
165165
op.packetHeader = read32(i + 4);
166-
166+
167167
// Verify parity
168168
if (!checkParity(op.streamHeader)) {
169-
llvm::errs() << "Stream header parity check failed at offset " << i << "\n";
169+
llvm::errs() << "Stream header parity check failed at offset " << i
170+
<< "\n";
170171
return std::nullopt;
171172
}
172173
if (!checkParity(op.packetHeader)) {
173-
llvm::errs() << "Packet header parity check failed at offset " << i + 4 << "\n";
174+
llvm::errs() << "Packet header parity check failed at offset " << i + 4
175+
<< "\n";
174176
return std::nullopt;
175177
}
176-
178+
177179
// Decode stream header fields
178180
op.pktType = (op.streamHeader >> 12) & 0x7;
179181
op.pktId = op.streamHeader & 0xFF;
180-
182+
181183
// Decode control packet header fields
182184
op.streamId = (op.packetHeader >> 24) & 0x7F;
183185
op.opcode = (op.packetHeader >> 22) & 0x3;
184186
op.beats = (op.packetHeader >> 20) & 0x3;
185187
op.address = op.packetHeader & 0xFFFFF;
186-
187-
i += 8; // consumed 2 words
188-
188+
189+
i += 8; // consumed 2 words
190+
189191
LLVM_DEBUG(llvm::dbgs() << "Control packet at offset " << (i - 8)
190192
<< ": opcode=" << static_cast<int>(op.opcode)
191193
<< " stream_id=" << static_cast<int>(op.streamId)
192194
<< " addr=0x" << llvm::format("%05X", op.address)
193195
<< " beats=" << static_cast<int>(op.beats) << "\n");
194-
196+
195197
// Read data payload if present (opcode 0x0=write or 0x2=blockwrite)
196198
if (op.opcode == 0x0 || op.opcode == 0x2) {
197199
uint32_t numDataWords = op.beats + 1;
198200
if (!requireBytes(i, numDataWords * 4)) {
199201
llvm::errs() << "Truncated data payload\n";
200202
return std::nullopt;
201203
}
202-
204+
203205
op.data.resize(numDataWords);
204206
for (uint32_t j = 0; j < numDataWords; j++) {
205207
op.data[j] = read32(i + j * 4);
206208
}
207209
i += numDataWords * 4;
208-
209-
LLVM_DEBUG(llvm::dbgs() << " Data: [";
210-
for (size_t j = 0; j < op.data.size(); j++) {
211-
if (j > 0) llvm::dbgs() << ", ";
212-
llvm::dbgs() << op.data[j];
213-
}
214-
llvm::dbgs() << "]\n");
210+
211+
LLVM_DEBUG(llvm::dbgs() << " Data: ["; for (size_t j = 0;
212+
j < op.data.size(); j++) {
213+
if (j > 0)
214+
llvm::dbgs() << ", ";
215+
llvm::dbgs() << op.data[j];
216+
} llvm::dbgs() << "]\n");
215217
}
216-
218+
217219
ops.push_back(std::move(op));
218220
}
219-
221+
220222
if (i != data.size()) {
221-
llvm::errs() << "Warning: " << (data.size() - i)
223+
llvm::errs() << "Warning: " << (data.size() - i)
222224
<< " bytes remaining after parsing control packets\n";
223225
}
224-
226+
225227
return ops;
226228
}
227229

@@ -898,11 +900,11 @@ xilinx::AIE::convertControlPacketBinaryToMLIR(mlir::MLIRContext *ctx,
898900
uint32_t colInt = (op.address >> targetModel.getColumnShift()) & 0x1f;
899901
uint32_t rowInt = (op.address >> targetModel.getRowShift()) & 0x1f;
900902
tileMap[key] = std::make_pair(colInt, rowInt);
901-
903+
902904
// Create tile and set controller_id attribute
903905
auto tile = TileOp::getOrCreate(builder, device, colInt, rowInt);
904-
auto packetInfoAttr = AIE::PacketInfoAttr::get(
905-
builder.getContext(), op.pktType, op.pktId);
906+
auto packetInfoAttr =
907+
AIE::PacketInfoAttr::get(builder.getContext(), op.pktType, op.pktId);
906908
tile->setAttr("controller_id", packetInfoAttr);
907909
}
908910
}
@@ -918,7 +920,7 @@ xilinx::AIE::convertControlPacketBinaryToMLIR(mlir::MLIRContext *ctx,
918920
for (const auto &op : operations) {
919921
IntegerAttr lengthAttr;
920922
DenseI32ArrayAttr dataAttr;
921-
923+
922924
if (op.opcode == 0x0 || op.opcode == 0x2) {
923925
// Write or blockwrite - has data payload
924926
SmallVector<int32_t> dataVec;
@@ -930,14 +932,11 @@ xilinx::AIE::convertControlPacketBinaryToMLIR(mlir::MLIRContext *ctx,
930932
// Read - has length but no data
931933
lengthAttr = builder.getI32IntegerAttr(op.beats + 1);
932934
}
933-
935+
934936
builder.create<AIEX::NpuControlPacketOp>(
935-
loc,
936-
builder.getUI32IntegerAttr(op.address),
937-
lengthAttr,
937+
loc, builder.getUI32IntegerAttr(op.address), lengthAttr,
938938
builder.getI32IntegerAttr(op.opcode),
939-
builder.getI32IntegerAttr(op.streamId),
940-
dataAttr);
939+
builder.getI32IntegerAttr(op.streamId), dataAttr);
941940
}
942941

943942
return module;

python/compiler/pkt2mlir/README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# pkt2mlir - Control Packet Binary to MLIR Disassembler
2+
3+
`pkt2mlir.py` is a tool that converts AIE NPU control packet binaries into human-readable MLIR format. This is useful for debugging, inspecting compiled configurations, and understanding the control packet operations sent to the NPU.
4+
5+
## Overview
6+
7+
The tool parses binary control packet files generated by `aie-translate --aie-ctrlpkt-to-bin` and reconstructs the equivalent MLIR module containing:
8+
- Device configuration (`aie.device`)
9+
- Tile controller_id attributes (`PacketInfoAttr`)
10+
- Runtime sequences with control packet operations (`aiex.control_packet`)
11+
12+
## Usage
13+
14+
```bash
15+
pkt2mlir.py -f <binary-file>
16+
```
17+
18+
### Options
19+
20+
- `-f`, `-file FILE` : Input control packet binary file (required)
21+
- `-h`, `--help` : Show help message and exit
22+
23+
## Examples
24+
25+
### Basic Control Packet Operations Round-trip
26+
27+
Generate control packet binary and disassemble it:
28+
29+
```bash
30+
# Create a simple MLIR file with control packets
31+
cat > control_packets.mlir << 'EOF'
32+
module {
33+
aie.device(npu1) {
34+
%tile00 = aie.tile(0, 0) {controller_id = #aie.packet_info<pkt_type = 2, pkt_id = 3>}
35+
36+
aiex.runtime_sequence() {
37+
aiex.control_packet {address = 0x0001F000 : ui32, opcode = 0 : i32, stream_id = 0 : i32, data = array<i32: 2>}
38+
aiex.control_packet {address = 0x0001F020 : ui32, opcode = 2 : i32, stream_id = 9 : i32, data = array<i32: 3, 4, 5, 6>}
39+
aiex.control_packet {address = 0x00000400 : ui32, opcode = 1 : i32, stream_id = 2 : i32, length = 4 : i32}
40+
}
41+
}
42+
}
43+
EOF
44+
45+
# Compile to binary
46+
aie-translate --aie-ctrlpkt-to-bin control_packets.mlir -o control_packets.bin
47+
```
48+
49+
View the raw control packet binary structure:
50+
51+
```bash
52+
# Inspect binary structure (2 word header + optional data for each packet)
53+
hexdump -C control_packets.bin
54+
```
55+
56+
```
57+
00000000 00 20 00 03 00 1f 00 00 02 00 00 00 00 20 00 03 |. .......... ..|
58+
00000010 09 b1 f0 20 03 00 00 00 04 00 00 00 05 00 00 00 |... ............|
59+
00000020 06 00 00 00 00 20 00 03 02 70 04 00 |..... ...p..|
60+
```
61+
62+
Translate back to MLIR:
63+
64+
```bash
65+
pkt2mlir.py -f control_packets.bin
66+
```
67+
68+
```mlir
69+
module {
70+
aie.device(npu1_1col) {
71+
%shim_noc_tile_0_0 = aie.tile(0, 0) {controller_id = #aie.packet_info<pkt_type = 2, pkt_id = 3>}
72+
73+
aiex.runtime_sequence @configure() {
74+
aiex.control_packet {address = 126976 : ui32, data = array<i32: 2>, opcode = 0 : i32, stream_id = 0 : i32}
75+
aiex.control_packet {address = 127008 : ui32, data = array<i32: 3, 4, 5, 6>, opcode = 2 : i32, stream_id = 9 : i32}
76+
aiex.control_packet {address = 1024 : ui32, length = 4 : i32, opcode = 1 : i32, stream_id = 2 : i32}
77+
}
78+
}
79+
}
80+
```
81+
82+
## Control Packet Binary Format
83+
84+
Each control packet in the binary consists of:
85+
86+
1. **Stream Header** (32 bits):
87+
- Bit [31]: Even parity bit
88+
- Bits [14:12]: Packet type (3 bits)
89+
- Bits [7:0]: Packet ID (8 bits)
90+
91+
2. **Control Packet Header** (32 bits):
92+
- Bit [31]: Even parity bit
93+
- Bits [30:24]: Stream ID (7 bits)
94+
- Bits [23:22]: Opcode (2 bits)
95+
- `0x0` = Write
96+
- `0x1` = Read
97+
- `0x2` = Block write
98+
- Bits [21:20]: Beats (size-1, 2 bits)
99+
- Bits [19:0]: Address (20 bits)
100+
101+
3. **Data Payload** (optional, 32-bit words):
102+
- Present for write (opcode 0x0) and block write (opcode 0x2)
103+
- Number of words = beats + 1
104+
105+
## Use Cases
106+
107+
- **Debugging**: Inspect what control packets are being sent to the NPU
108+
- **Verification**: Compare generated binaries against expected values
109+
- **Reverse Engineering**: Understand control packet configurations
110+
- **Testing**: Validate round-trip conversion (MLIR → binary → MLIR)
111+
- **Documentation**: Generate human-readable documentation from binaries
112+
113+
## See Also
114+
115+
- `txn2mlir.py` - Transaction binary to MLIR disassembler
116+
- `aie-translate --aie-ctrlpkt-to-bin` - Generate control packet binaries
117+
- `aie-translate --aie-npu-to-binary` - Generate transaction binaries

0 commit comments

Comments
 (0)