Skip to content

Commit 1b31756

Browse files
committed
format
1 parent 07ce0a8 commit 1b31756

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
@@ -53,10 +53,10 @@ struct TransactionBinaryOperation {
5353
// A ControlPacketOperation encapsulates a parsed control packet including
5454
// stream header, control packet header, and optional data payload.
5555
struct ControlPacketOperation {
56-
uint32_t streamHeader; // word 0: parity + pkt_type + pkt_id
57-
uint32_t packetHeader; // word 1: parity + stream_id + opcode + beats + addr
58-
std::vector<uint32_t> data; // payload words (if opcode is write/blockwrite)
59-
56+
uint32_t streamHeader; // word 0: parity + pkt_type + pkt_id
57+
uint32_t packetHeader; // word 1: parity + stream_id + opcode + beats + addr
58+
std::vector<uint32_t> data; // payload words (if opcode is write/blockwrite)
59+
6060
// Decoded fields
6161
uint8_t pktType;
6262
uint8_t pktId;
@@ -88,90 +88,92 @@ static bool checkParity(uint32_t word) {
8888
static std::optional<std::vector<ControlPacketOperation>>
8989
parseControlPacket(const std::vector<uint8_t> &data) {
9090
std::vector<ControlPacketOperation> ops;
91-
91+
9292
size_t i = 0;
93-
93+
9494
auto requireBytes = [&](size_t offset, size_t length) -> bool {
9595
if (offset + length > data.size()) {
9696
llvm::errs() << "Control packet binary truncated\n";
9797
return false;
9898
}
9999
return true;
100100
};
101-
101+
102102
auto read32 = [&](size_t offset) -> uint32_t {
103103
uint32_t value;
104104
std::memcpy(&value, data.data() + offset, sizeof(uint32_t));
105105
return value;
106106
};
107-
108-
while (i + 8 <= data.size()) { // Need at least 2 words (stream hdr + pkt hdr)
107+
108+
while (i + 8 <= data.size()) { // Need at least 2 words (stream hdr + pkt hdr)
109109
ControlPacketOperation op;
110-
110+
111111
// Read stream header (word 0)
112112
op.streamHeader = read32(i);
113-
113+
114114
// Read control packet header (word 1)
115115
op.packetHeader = read32(i + 4);
116-
116+
117117
// Verify parity
118118
if (!checkParity(op.streamHeader)) {
119-
llvm::errs() << "Stream header parity check failed at offset " << i << "\n";
119+
llvm::errs() << "Stream header parity check failed at offset " << i
120+
<< "\n";
120121
return std::nullopt;
121122
}
122123
if (!checkParity(op.packetHeader)) {
123-
llvm::errs() << "Packet header parity check failed at offset " << i + 4 << "\n";
124+
llvm::errs() << "Packet header parity check failed at offset " << i + 4
125+
<< "\n";
124126
return std::nullopt;
125127
}
126-
128+
127129
// Decode stream header fields
128130
op.pktType = (op.streamHeader >> 12) & 0x7;
129131
op.pktId = op.streamHeader & 0xFF;
130-
132+
131133
// Decode control packet header fields
132134
op.streamId = (op.packetHeader >> 24) & 0x7F;
133135
op.opcode = (op.packetHeader >> 22) & 0x3;
134136
op.beats = (op.packetHeader >> 20) & 0x3;
135137
op.address = op.packetHeader & 0xFFFFF;
136-
137-
i += 8; // consumed 2 words
138-
138+
139+
i += 8; // consumed 2 words
140+
139141
LLVM_DEBUG(llvm::dbgs() << "Control packet at offset " << (i - 8)
140142
<< ": opcode=" << static_cast<int>(op.opcode)
141143
<< " stream_id=" << static_cast<int>(op.streamId)
142144
<< " addr=0x" << llvm::format("%05X", op.address)
143145
<< " beats=" << static_cast<int>(op.beats) << "\n");
144-
146+
145147
// Read data payload if present (opcode 0x0=write or 0x2=blockwrite)
146148
if (op.opcode == 0x0 || op.opcode == 0x2) {
147149
uint32_t numDataWords = op.beats + 1;
148150
if (!requireBytes(i, numDataWords * 4)) {
149151
llvm::errs() << "Truncated data payload\n";
150152
return std::nullopt;
151153
}
152-
154+
153155
op.data.resize(numDataWords);
154156
for (uint32_t j = 0; j < numDataWords; j++) {
155157
op.data[j] = read32(i + j * 4);
156158
}
157159
i += numDataWords * 4;
158-
159-
LLVM_DEBUG(llvm::dbgs() << " Data: [";
160-
for (size_t j = 0; j < op.data.size(); j++) {
161-
if (j > 0) llvm::dbgs() << ", ";
162-
llvm::dbgs() << op.data[j];
163-
}
164-
llvm::dbgs() << "]\n");
160+
161+
LLVM_DEBUG(llvm::dbgs() << " Data: ["; for (size_t j = 0;
162+
j < op.data.size(); j++) {
163+
if (j > 0)
164+
llvm::dbgs() << ", ";
165+
llvm::dbgs() << op.data[j];
166+
} llvm::dbgs() << "]\n");
165167
}
166-
168+
167169
ops.push_back(std::move(op));
168170
}
169-
171+
170172
if (i != data.size()) {
171-
llvm::errs() << "Warning: " << (data.size() - i)
173+
llvm::errs() << "Warning: " << (data.size() - i)
172174
<< " bytes remaining after parsing control packets\n";
173175
}
174-
176+
175177
return ops;
176178
}
177179

@@ -619,11 +621,11 @@ xilinx::AIE::convertControlPacketBinaryToMLIR(mlir::MLIRContext *ctx,
619621
uint32_t colInt = (op.address >> targetModel.getColumnShift()) & 0x1f;
620622
uint32_t rowInt = (op.address >> targetModel.getRowShift()) & 0x1f;
621623
tileMap[key] = std::make_pair(colInt, rowInt);
622-
624+
623625
// Create tile and set controller_id attribute
624626
auto tile = TileOp::getOrCreate(builder, device, colInt, rowInt);
625-
auto packetInfoAttr = AIE::PacketInfoAttr::get(
626-
builder.getContext(), op.pktType, op.pktId);
627+
auto packetInfoAttr =
628+
AIE::PacketInfoAttr::get(builder.getContext(), op.pktType, op.pktId);
627629
tile->setAttr("controller_id", packetInfoAttr);
628630
}
629631
}
@@ -639,7 +641,7 @@ xilinx::AIE::convertControlPacketBinaryToMLIR(mlir::MLIRContext *ctx,
639641
for (const auto &op : operations) {
640642
IntegerAttr lengthAttr;
641643
DenseI32ArrayAttr dataAttr;
642-
644+
643645
if (op.opcode == 0x0 || op.opcode == 0x2) {
644646
// Write or blockwrite - has data payload
645647
SmallVector<int32_t> dataVec;
@@ -651,14 +653,11 @@ xilinx::AIE::convertControlPacketBinaryToMLIR(mlir::MLIRContext *ctx,
651653
// Read - has length but no data
652654
lengthAttr = builder.getI32IntegerAttr(op.beats + 1);
653655
}
654-
656+
655657
builder.create<AIEX::NpuControlPacketOp>(
656-
loc,
657-
builder.getUI32IntegerAttr(op.address),
658-
lengthAttr,
658+
loc, builder.getUI32IntegerAttr(op.address), lengthAttr,
659659
builder.getI32IntegerAttr(op.opcode),
660-
builder.getI32IntegerAttr(op.streamId),
661-
dataAttr);
660+
builder.getI32IntegerAttr(op.streamId), dataAttr);
662661
}
663662

664663
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)