Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
86 changes: 84 additions & 2 deletions lib/NeuraDialect/Transforms/GenerateCodePass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ struct GenerateCodePass

StringRef getArgument() const override { return "generate-code"; }
StringRef getDescription() const override {
return "CGRA YAML/ASM gen (multi-hop routers + endpoint register deposit + timing-aware rewiring, with CTRL_MOV kept).";
return "CGRA YAML/ASM gen (multi-hop routers + endpoint register deposit + timing-aware rewiring, with CTRL_MOV kept). Validates single-read-port constraint for reg->outport bypass.";
}
void getDependentDialects(DialectRegistry &registry) const override {
registry.insert<mlir::func::FuncDialect>();
Expand All @@ -372,6 +372,30 @@ struct GenerateCodePass
std::unordered_set<uint64_t> deposit_signatures; // (dstTileId, time_step, regId).
std::unordered_set<uint64_t> egress_signatures; // (srcTileId, time_step, regId, out_dir).

// Tracks register reads from routing bypass paths (reg -> outport via
// hardware direct path, bypassing the FU). Used for single-read-port
// constraint validation: if a computation and a routing bypass both
// read from the same register cluster in the same time step, they
// must read the identical register index.
struct BypassRead {
int col_idx, row_idx;
int index_per_ii;
int reg_id;
};
SmallVector<BypassRead, 8> bypass_reads;

static constexpr int kRegsPerCluster = 8;
struct RegClusterInfo { int cluster_no; int intra_index; };
static RegClusterInfo getRegClusterInfo(int reg_id) {
return {reg_id / kRegsPerCluster, reg_id % kRegsPerCluster};
}
static int parseRegIdFromOperand(const std::string &operand) {
if (operand.size() < 2 || operand[0] != '$') return -1;
for (size_t i = 1; i < operand.size(); ++i)
if (!std::isdigit(static_cast<unsigned char>(operand[i]))) return -1;
return std::stoi(operand.substr(1));
}

// ---------- helpers to place materialized instructions ----------.
void placeRouterHop(const Topology &topology, int tile_id, int time_step,
StringRef input_direction, StringRef output_direction,
Expand All @@ -397,6 +421,7 @@ struct GenerateCodePass
hop_signatures.clear();
deposit_signatures.clear();
egress_signatures.clear();
bypass_reads.clear();
instruction_id_map.clear();
next_instruction_id = 0;
timing_field_error = false;
Expand Down Expand Up @@ -793,6 +818,12 @@ struct GenerateCodePass
placeSrcEgress(topo, src_tile, first_link_time_step, basics.producer_direction,
basics.reg_split.src_reg_step->regId,
/*asCtrlMov=*/IsCtrl, egress_instruction_id);
if constexpr (!IsCtrl) {
auto [tile_x, tile_y] = topo.tile_location.lookup(src_tile);
bypass_reads.push_back({tile_x, tile_y,
indexPerIiFromTimeStep(first_link_time_step),
basics.reg_split.src_reg_step->regId});
}
}
size_t hop_counter = 1;
generateIntermediateHops<IsCtrl>(basics.links, topo, basics.mov_dfg_id, hop_counter);
Expand Down Expand Up @@ -1151,7 +1182,6 @@ struct GenerateCodePass
int egress_id = base;
int src_tile_id = topology.srcTileOfLink(link_steps.front().link_id);
auto coord = topology.tile_location.lookup(src_tile_id);
// Add placeholder node now (will be filled/confirmed by injectMaterializedInstructionNodes).
if (!nodes.count(egress_id)) {
nodes[egress_id] = DfgNodeInfo{
isCtrlMov(operation) ? "CTRL_MOV" : "DATA_MOV",
Expand Down Expand Up @@ -1694,6 +1724,52 @@ struct GenerateCodePass
inst->dst_operands.emplace_back(text, "RED");
}

// Validates the single-read-port constraint for register routing bypass.
// The hardware provides a direct path from each register bank to the
// routing crossbar (bypassing the FU). Since each register bank has
// only one read port, if a computation (towards FU) and a routing
// bypass (towards routing crossbar) both read from the SAME register
// cluster in the same tile and time step, they MUST read the identical
// register index.
//
// Note: This constraint only applies to same-tile routing bypasses
// (cross-tile bypasses that use links may have different constraints).
// However, due to the way the mapper works, cross-tile routing bypasses
// with src_reg_step are also tracked and validated for consistency.
void validateRegReadConstraints() {
for (const BypassRead &bypass : bypass_reads) {
RegClusterInfo bypass_info = getRegClusterInfo(bypass.reg_id);
auto tile_it = tile_time_instructions.find({bypass.col_idx, bypass.row_idx});
if (tile_it == tile_time_instructions.end()) continue;
auto ts_it = tile_it->second.find(bypass.index_per_ii);
if (ts_it == tile_it->second.end()) continue;

for (const Instruction &inst : ts_it->second) {
if (inst.opcode == "DATA_MOV" || inst.opcode == "CTRL_MOV") continue;

for (const Operand &src : inst.src_operands) {
int comp_reg_id = parseRegIdFromOperand(src.operand);
if (comp_reg_id < 0) continue;
RegClusterInfo comp_info = getRegClusterInfo(comp_reg_id);
if (comp_info.cluster_no != bypass_info.cluster_no) continue;
if (comp_info.intra_index != bypass_info.intra_index) {
llvm::errs()
<< "[WARNING] Register read-port conflict at tile("
<< bypass.col_idx << "," << bypass.row_idx
<< ") index_per_ii=" << bypass.index_per_ii
<< ": computation reads register $" << comp_reg_id
<< " (cluster " << comp_info.cluster_no
<< ", index " << comp_info.intra_index
<< ") but routing bypass reads register $" << bypass.reg_id
<< " (cluster " << bypass_info.cluster_no
<< ", index " << bypass_info.intra_index
<< "). Both should read the identical register if from same cluster.\n";
}
}
}
}
}

// ---------- entry point ----------.
void runOnOperation() override {
ModuleOp module = getOperation();
Expand All @@ -1719,6 +1795,12 @@ struct GenerateCodePass
expandMovImpl<false>(op, topo, /*unused*/reserve_to_phi_map);
for (Operation *op : ctrl_movs)
expandMovImpl<true>(op, topo, reserve_to_phi_map);

// Validates the single-read-port constraint for register routing bypass
// paths: same-cluster reads from computation and routing bypass must
// use the identical register index.
validateRegReadConstraints();

logUnresolvedOperands();

std::unordered_set<int> materialized_ids;
Expand Down
Loading
Loading