diff --git a/include/ArchitectureSpec/architecture.json b/include/ArchitectureSpec/architecture.json deleted file mode 100644 index bd995643..00000000 --- a/include/ArchitectureSpec/architecture.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "architecture": { - "name": "NeuraCGRA", - "version": "1.0", - "width": 4, - "height": 4 - }, - "tile_defaults": { - "num_registers": 64, - "default_ports": [ - "N", - "S", - "W", - "E" - ], - "operations": [ - "add", - "mul", - "sub" - ] - }, - "link_defaults": { - "latency": 1, - "bandwidth": 32 - }, - "connectivity": "mesh", - "link_overrides": [ - { - "id": 1, - "latency": 2, - "bandwidth": 32, - "src_tile_id": 0, - "dst_tile_id": 9, - "existence": true - }, - { - "id": 2, - "latency": 3, - "bandwidth": 32, - "src_tile_id": 0, - "dst_tile_id": 8, - "existence": true - } - ], - "tile_overrides": [ - { - "id": 0, - "x": 0, - "y": 0, - "operations": [ - "load" - ], - "num_registers": 32, - "ports": [ - "S", - "E" - ] - }, - { - "id": 3, - "x": 3, - "y": 0, - "operations": [ - "store" - ], - "num_registers": 32, - "ports": [ - "W" - ] - }, - { - "id": 5, - "x": 1, - "y": 1, - "operations": [ - "add", - "mul" - ], - "num_registers": 32, - "memory": { - "capacity": 2048 - }, - "ports": [ - "N", - "S", - "E", - "W" - ] - }, - { - "id": 6, - "x": 2, - "y": 1, - "operations": [], - "num_registers": 32, - "ports": [ - "E", - "W" - ] - } - ], - "extensions": { - "crossbar": false - }, - "simulator": { - "execution_model": "serial", - "logging": { - "enabled": true, - "file": "output.log" - }, - "driver": { - "name": "Driver", - "frequency": "1GHz" - }, - "device": { - "name": "Device", - "frequency": "1GHz", - "bind_to_architecture": true - } - } -} \ No newline at end of file diff --git a/include/ArchitectureSpec/yaml2json.py b/include/ArchitectureSpec/yaml2json.py deleted file mode 100644 index 1f9b8785..00000000 --- a/include/ArchitectureSpec/yaml2json.py +++ /dev/null @@ -1,33 +0,0 @@ -import yaml -import json -import sys -import os - -def convert_yaml_to_json(yaml_path, json_path=None): - if not os.path.isfile(yaml_path): - print(f"Error: File '{yaml_path}' not found.") - return - - with open(yaml_path, 'r') as f: - try: - data = yaml.safe_load(f) - except yaml.YAMLError as e: - print(f"YAML parse error: {e}") - return - - if json_path is None: - json_path = os.path.splitext(yaml_path)[0] + ".json" - - with open(json_path, 'w') as f: - json.dump(data, f, indent=2) - - print(f"✅ Converted '{yaml_path}' to '{json_path}'") - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Usage: python yaml2json.py [output.json]") - sys.exit(1) - - yaml_file = sys.argv[1] - json_file = sys.argv[2] if len(sys.argv) > 2 else None - convert_yaml_to_json(yaml_file, json_file) diff --git a/lib/NeuraDialect/Transforms/CMakeLists.txt b/lib/NeuraDialect/Transforms/CMakeLists.txt index 17bf2012..d5073678 100644 --- a/lib/NeuraDialect/Transforms/CMakeLists.txt +++ b/lib/NeuraDialect/Transforms/CMakeLists.txt @@ -25,4 +25,5 @@ add_mlir_library( MLIRTransforms MLIRNeura ${dialect_libs} + LLVMSupport ) \ No newline at end of file diff --git a/lib/NeuraDialect/Transforms/GenerateCodePass.cpp b/lib/NeuraDialect/Transforms/GenerateCodePass.cpp index a43d006d..086294ed 100644 --- a/lib/NeuraDialect/Transforms/GenerateCodePass.cpp +++ b/lib/NeuraDialect/Transforms/GenerateCodePass.cpp @@ -1,154 +1,723 @@ -#include "NeuraDialect/NeuraDialect.h" -#include "NeuraDialect/NeuraOps.h" -#include "NeuraDialect/NeuraPasses.h" -#include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Pass/Pass.h" -#include "llvm/Support/JSON.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/Operation.h" +#include "mlir/IR/Value.h" +#include "mlir/IR/Attributes.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" -using namespace mlir; -using namespace mlir::neura; +#include +#include +#include +#include +#include +#include +#include -#define GEN_PASS_DEF_GenerateCode -#include "NeuraDialect/NeuraPasses.h.inc" +#include "NeuraDialect/Architecture/Architecture.h" +#include "NeuraDialect/NeuraOps.h" + +using namespace mlir; +using namespace neura; namespace { +struct Operand { + std::string operand; + std::string color; + Operand(const std::string &op, const std::string &c = "RED") + : operand(op), color(c) {} +}; + +struct Instruction { + std::string opcode; + std::vector src_operands; + std::vector dst_operands; + int time_step = -1; // for ordering. + Instruction(const std::string &op) : opcode(op) {} +}; + +// A single-entry model per tile for now. Can be extended later. +struct Entry { + std::string entry_id; + std::string type; + std::vector instructions; + Entry(const std::string &id, const std::string &t = "loop") + : entry_id(id), type(t) {} +}; + +struct Tile { + int col_idx, row_idx; + int core_id; + Entry entry; // single entry per tile. + Tile(int c, int r, int id) : col_idx(c), row_idx(r), core_id(id), entry("entry0", "loop") {} +}; + +struct ArrayConfig { + int columns; + int rows; + std::vector cores; +}; + +struct TileLocation { + int col_idx = -1, row_idx = -1, time_step = -1; + bool has_tile = false; +}; + +// ---- Operation kind helpers ----. +static bool isDataMov(Operation *op) { return dyn_cast(op) != nullptr; } +static bool isCtrlMov(Operation *op) { return dyn_cast(op) != nullptr; } +static bool isPhi(Operation *op) { return dyn_cast(op) != nullptr; } +static bool isReserve(Operation *op) { return dyn_cast(op) != nullptr; } +static bool isConstant(Operation *op) { return dyn_cast(op) != nullptr; } + +// ----- placement helpers -----. +static TileLocation getTileLocation(Operation *op) { + TileLocation tile_location; + if (auto arr = op->getAttrOfType("mapping_locs")) { + for (Attribute a : arr) { + auto d = dyn_cast(a); + if (!d) continue; + auto resource = dyn_cast_or_null(d.get("resource")); + if (!resource || resource.getValue() != "tile") continue; + if (auto x_coord = dyn_cast_or_null(d.get("x"))) tile_location.col_idx = x_coord.getInt(); + if (auto y_coord = dyn_cast_or_null(d.get("y"))) tile_location.row_idx = y_coord.getInt(); + if (auto timestep = dyn_cast_or_null(d.get("time_step"))) tile_location.time_step = timestep.getInt(); + tile_location.has_tile = true; + break; + } + } + // If tile mappings exist, x/y must be valid. + assert(!tile_location.has_tile || (tile_location.col_idx >= 0 && tile_location.row_idx >= 0)); + return tile_location; +} + +// Helper to get mapping_locations arrays. +static ArrayAttr getMappingLocations(Operation *op) { + return op->getAttrOfType("mapping_locs"); +} + +static std::optional getMappedRegId(Operation *op) { + if (auto mapping_locations = getMappingLocations(op)) { + for (Attribute location_attr : mapping_locations) { + auto location_dict = dyn_cast(location_attr); + if (!location_dict) continue; + auto resource_attr = dyn_cast_or_null(location_dict.get("resource")); + if (!resource_attr) continue; + if (resource_attr.getValue() == "register" || resource_attr.getValue() == "reg") { + if (auto register_id = dyn_cast_or_null(location_dict.get("id"))) + return register_id.getInt(); + } + } + } + return std::nullopt; +} + +static std::string getOpcode(Operation *op) { + std::string opcode = op->getName().getStringRef().str(); + if (opcode.rfind("neura.", 0) == 0) opcode = opcode.substr(6); + if (isConstant(op)) return "CONSTANT"; + std::transform(opcode.begin(), opcode.end(), opcode.begin(), ::toupper); + return opcode; +} + +// Literals for CONSTANT's sources, e.g. "#10" / "#0" / "#3.0". +static std::string getConstantLiteral(Operation *op) { + if (!isConstant(op)) return ""; + if (auto value_attr = op->getAttr("value")) { + if (auto integer_attr = dyn_cast(value_attr)) + return "#" + std::to_string(integer_attr.getInt()); + if (auto float_attr = dyn_cast(value_attr)) + return "#" + std::to_string(float_attr.getValueAsDouble()); + } + return "#0"; +} + +// ----- Topology from Architecture -----. +struct Topology { + DenseMap> link_ends; // link_id -> (srcTileId, dstTileId). + DenseMap> tile_location; // tileId -> (x,y). + DenseMap, int> coord_to_tile; // (x,y) -> tileId. + + StringRef getDirBetween(int srcTid, int dstTid) const { + auto [src_x, src_y] = tile_location.lookup(srcTid); + auto [dst_x, dst_y] = tile_location.lookup(dstTid); + int dc = dst_x - src_x, dr = dst_y - src_y; + if (dc == 1 && dr == 0) return "EAST"; + if (dc == -1 && dr == 0) return "WEST"; + if (dc == 0 && dr == 1) return "NORTH"; + if (dc == 0 && dr == -1) return "SOUTH"; + return "LOCAL"; + } + StringRef dirFromLink(int link_id) const { + auto it = link_ends.find(link_id); + if (it == link_ends.end()) return "LOCAL"; + return getDirBetween(it->second.first, it->second.second); + } + StringRef invertDir(StringRef d) const { + if (d == "EAST") return "WEST"; + if (d == "WEST") return "EAST"; + if (d == "NORTH") return "SOUTH"; + if (d == "SOUTH") return "NORTH"; + return "LOCAL"; + } + int srcTileOfLink(int link_id) const { return link_ends.lookup(link_id).first; } + int dstTileOfLink(int link_id) const { return link_ends.lookup(link_id).second; } + int tileIdAt(int x, int y) const { + auto it = coord_to_tile.find({x,y}); + return (it == coord_to_tile.end()) ? -1 : it->second; + } +}; + +static Topology getTopologyFromArchitecture(int columns, int rows) { + Topology topo; + mlir::neura::Architecture arch(columns, rows); + + for (auto *tile : arch.getAllTiles()) { + topo.tile_location[tile->getId()] = {tile->getX(), tile->getY()}; + topo.coord_to_tile[{tile->getX(), tile->getY()}] = tile->getId(); + } + for (auto *link : arch.getAllLinks()) { + auto *src_tile = link->getSrcTile(); + auto *dst_tile = link->getDstTile(); + topo.link_ends[link->getId()] = {src_tile->getId(), dst_tile->getId()}; + } + return topo; +} + +// ----- Extract mapping steps (sorted by time) -----. +struct LinkStep { int link_id; int ts; }; +struct RegStep { int regId; int ts; }; + +static SmallVector collectLinkSteps(Operation *op) { + SmallVector steps; + if (auto mapping_locations = getMappingLocations(op)) { + for (Attribute location_attr : mapping_locations) { + auto location_dict = dyn_cast(location_attr); + if (!location_dict) continue; + auto resource_attr = dyn_cast_or_null(location_dict.get("resource")); + if (!resource_attr || resource_attr.getValue() != "link") continue; + auto link_id = dyn_cast_or_null(location_dict.get("id")); + auto time_step = dyn_cast_or_null(location_dict.get("time_step")); + if (!link_id || !time_step) continue; + steps.push_back({(int)link_id.getInt(), (int)time_step.getInt()}); + } + } + llvm::sort(steps, [](const LinkStep &a, const LinkStep &b){ return a.ts < b.ts; }); + return steps; +} + +static SmallVector collectRegSteps(Operation *op) { + SmallVector steps; + if (auto mapping_locations = getMappingLocations(op)) { + for (Attribute location_attr : mapping_locations) { + auto location_dict = dyn_cast(location_attr); + if (!location_dict) continue; + auto resource_attr = dyn_cast_or_null(location_dict.get("resource")); + if (!resource_attr) continue; + if (resource_attr.getValue() == "register" || resource_attr.getValue() == "reg") { + auto register_id = dyn_cast_or_null(location_dict.get("id")); + auto time_step = dyn_cast_or_null(location_dict.get("time_step")); + if (!register_id || !time_step) continue; + steps.push_back({(int)register_id.getInt(), (int)time_step.getInt()}); + } + } + } + llvm::sort(steps, [](const RegStep &a, const RegStep &b){ return a.ts < b.ts; }); + return steps; +} + +// ----- Pass -----. +struct InstructionReference { int col_idx, row_idx, t, idx; }; + struct GenerateCodePass : public PassWrapper> { MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(GenerateCodePass) StringRef getArgument() const override { return "generate-code"; } StringRef getDescription() const override { - return "Generates JSON code from mapped Neura IR."; + return "CGRA YAML/ASM gen (multi-hop routers + endpoint register deposit + timing-aware rewiring, with CTRL_MOV kept)."; } - void getDependentDialects(DialectRegistry ®istry) const override { - registry.insert(); + registry.insert(); } - void runOnOperation() override { - ModuleOp module = getOperation(); + DenseMap operation_placements; - llvm::json::Array functions_array; + // Maps of tile coordinates (x,y) -> time_step -> vector of Instructions. + std::map, std::map>> tile_time_instructions; - for (auto func : module.getOps()) { - auto accel_attr = func->getAttrOfType("accelerator"); - if (!accel_attr || accel_attr.getValue() != "neura") { - continue; + // Back references from IR operations to emitted instructions. + DenseMap operation_to_instruction_reference; + DenseMap> operation_to_operands; + + // De-dup sets. + std::unordered_set hop_signatures; // (midTileId, ts, link_id). + std::unordered_set deposit_signatures; // (dstTileId, ts, regId). + + // ---------- helpers to place materialized instructions ----------. + void placeRouterHop(const Topology &topology, int tile_id, int time_step, + StringRef input_direction, StringRef output_direction, bool asCtrlMov = false) { + auto [tile_x, tile_y] = topology.tile_location.lookup(tile_id); + Instruction instruction(asCtrlMov ? "CTRL_MOV" : "DATA_MOV"); + instruction.time_step = time_step; + instruction.src_operands.emplace_back(input_direction.str(), "RED"); + instruction.dst_operands.emplace_back(output_direction.str(), "RED"); + tile_time_instructions[{tile_x, tile_y}][time_step].push_back(std::move(instruction)); + } + + // ---------- initialization helpers ----------. + void clearState() { + operation_placements.clear(); + tile_time_instructions.clear(); + operation_to_instruction_reference.clear(); + operation_to_operands.clear(); + hop_signatures.clear(); + deposit_signatures.clear(); + } + + std::pair getArrayDimensions(func::FuncOp function) { + int columns = 4, rows = 4; // default 4x4 CGRA. + if (auto mapping_info = function->getAttrOfType("mapping_info")) { + if (auto x_tiles = dyn_cast_or_null(mapping_info.get("x_tiles"))) columns = x_tiles.getInt(); + if (auto y_tiles = dyn_cast_or_null(mapping_info.get("y_tiles"))) rows = y_tiles.getInt(); + } + return {columns, rows}; + } + + // ---------- Single-walk indexing ----------. + // Do everything that needs walks in a single pass:. + // - record operation_placements. + // - materialize compute/phi/const instructions. + // - collect DATA_MOV and CTRL_MOV ops. + // - collect reserve_to_phi_maps (PHI's operand#0 is the reserve). + void indexIR(func::FuncOp function, + SmallVector &dataMovs, + SmallVector &ctrlMovs, + DenseMap &reserve_to_phi_map) { + function.walk([&](Operation *op) { + // placement for every op (even for mov/reserve). + operation_placements[op] = getTileLocation(op); + + // build reserve -> phi mapping. + if (isPhi(op) && op->getNumOperands() >= 1) { + reserve_to_phi_map[op->getOperand(0)] = op; } - llvm::json::Object func_obj; - func_obj["name"] = func.getName().str(); + // collect forwarders. + if (isDataMov(op)) { dataMovs.push_back(op); return; } + if (isCtrlMov(op)) { ctrlMovs.push_back(op); return; } - if (auto ii_attr = func->getAttrOfType("CompiledII")) { - func_obj["CompiledII"] = ii_attr.getInt(); + // skip Reserve from materialization. + if (isReserve(op)) return; + + // materialize all other ops placed on tiles (compute/phi/const/etc.). + auto placement = operation_placements[op]; + if (!placement.has_tile) return; + + std::string opcode = getOpcode(op); + Instruction inst(opcode); + inst.time_step = placement.time_step; + + if (isConstant(op)) { + inst.src_operands.emplace_back(getConstantLiteral(op), "RED"); + } else { + SmallVector operands; operands.reserve(op->getNumOperands()); + for (Value v : op->getOperands()) { + operands.push_back(v); + inst.src_operands.emplace_back("UNRESOLVED", "RED"); + } + operation_to_operands[op] = std::move(operands); } - if (auto recMII_attr = func->getAttrOfType("RecMII")) { - func_obj["RecMII"] = recMII_attr.getInt(); + + if (auto mapped_register_id = getMappedRegId(op)) + inst.dst_operands.emplace_back("$" + std::to_string(*mapped_register_id), "RED"); + + auto &bucket = getInstructionBucket(placement.col_idx, placement.row_idx, placement.time_step); + bucket.push_back(std::move(inst)); + operation_to_instruction_reference[op] = + InstructionReference{placement.col_idx, placement.row_idx, placement.time_step, + (int)bucket.size() - 1}; + }); + } + + // ---------- unified forwarder expansion helpers ----------. + static SmallVector getLinkChain(Operation *forwarder) { return collectLinkSteps(forwarder); } + static SmallVector getRegisterSteps(Operation *forwarder) { return collectRegSteps(forwarder); } + + // Validate forwarder op arities: DATA_MOV: at least 1 in/1 out; CTRL_MOV: at least 2 inputs (src,reserve). + template + bool validateForwarderShape(Operation *forwarder) { + if constexpr (!IsCtrl) { + return forwarder->getNumOperands() >= 1 && forwarder->getNumResults() >= 1; + } else { + return forwarder->getNumOperands() >= 2; + } + } + + // Compute producer first-hop directions and consumer last-hop directions (or LOCAL if link-less). + std::pair computeDirections(const SmallVector &links, const Topology &topo) { + StringRef producer_direction("LOCAL"); + StringRef consumer_direction("LOCAL"); + if (!links.empty()) { + producer_direction = topo.dirFromLink(links.front().link_id); + consumer_direction = topo.invertDir(topo.dirFromLink(links.back().link_id)); + } + return {producer_direction, consumer_direction}; + } + + // Add producer endpoints (first-hop directions or local $reg when using same-tile register paths). + void setProducerDestination(Operation *producer, StringRef producer_direction, const SmallVector ®s) { + if (auto *pi = getInstructionPointer(producer)) { + if (!producer_direction.empty() && producer_direction != "LOCAL") { + setUniqueDestination(pi, producer_direction.str()); + } else if (!regs.empty()) { + setUniqueDestination(pi, "$" + std::to_string(regs.back().regId)); } - if (auto resMII_attr = func->getAttrOfType("ResMII")) { - func_obj["ResMII"] = resMII_attr.getInt(); + } + } + + // Emit router hops for multi-hop paths (from the second hop onwards). CTRL_MOV emits CTRL_MOV hops. + template + void generateIntermediateHops(const SmallVector &links, const Topology &topo) { + for (size_t i = 1; i < links.size(); ++i) { + int prev_link = links[i - 1].link_id; + int cur_link = links[i].link_id; + int ts = links[i].ts; + + int mid_tile = topo.srcTileOfLink(cur_link); + StringRef in = topo.invertDir(topo.dirFromLink(prev_link)); + StringRef out = topo.dirFromLink(cur_link); + + uint64_t sig = (uint64_t)mid_tile << 32 ^ (uint64_t)ts << 16 ^ (uint64_t)cur_link; + if (hop_signatures.insert(sig).second) + placeRouterHop(topo, mid_tile, ts, in, out, /*asCtrlMov=*/IsCtrl); + } + } + + // Consumers for DATA_MOV: all users of forwarder results(0). + SmallVector, 2> collectDataMovConsumers(Operation *forwarder) { + SmallVector, 2> consumers; + Value out = forwarder->getResult(0); + for (OpOperand &use : out.getUses()) + consumers.push_back({use.getOwner(), use.get()}); + return consumers; + } + + // Consumers for CTRL_MOV: find PHI via reserve->phi maps; wire the PHI's *data* inputs (sources). + SmallVector, 2> collectCtrlMovConsumers(Operation *forwarder, + const DenseMap &reserve2phi) { + SmallVector, 2> consumers; + Value reserve = forwarder->getOperand(1); + Value source = forwarder->getOperand(0); + if (Operation *phi = reserve2phi.lookup(reserve)) + consumers.push_back({phi, source}); + else + forwarder->emitWarning("ctrl_mov dest is not consumed by a PHI operand#0; skipping."); + return consumers; + } + + // Try register-based rewiring. If cross-tile, emit deposits [incoming_dir]->[$reg] at earliest reg ts. + // Returns true if rewiring to $reg was applied to consumers. + template + bool handleRegisterRewiring(Operation *consOp, Value atVal, const SmallVector ®s, + const SmallVector &links, const Topology &topo) { + if (regs.empty()) return false; + + int timestep_0 = regs.front().ts; + int register_id = regs.back().regId; + + if (!links.empty()) { + // Cross-tile: deposit on destination tile at earliest register ts. + int dst_tile = topo.dstTileOfLink(links.back().link_id); + StringRef incoming_dir = topo.dirFromLink(links.back().link_id); + placeDstDeposit(topo, dst_tile, timestep_0, incoming_dir, register_id, /*asCtrlMov=*/IsCtrl); + + auto cp = operation_placements.lookup(consOp); + if (cp.has_tile && cp.time_step > timestep_0) { + setConsumerSourceExact(consOp, atVal, "$" + std::to_string(register_id)); + return true; } + } else { + // Same-tile: must go via register. + setConsumerSourceExact(consOp, atVal, "$" + std::to_string(register_id)); + return true; + } + return false; + } - llvm::json::Array op_array; + template + void handleDirectionRewiring(Operation *consOp, Value atVal, StringRef consumer_direction, + const SmallVector &links, Operation *forwarder) { + if (!links.empty()) { + setConsumerSourceExact(consOp, atVal, consumer_direction.str()); + } else { + forwarder->emitError(IsCtrl + ? "same-tile ctrl_mov without register mapping is illegal. Provide a register in mapping_locs." + : "same-tile data_mov without register mapping is illegal. Provide a register in mapping_locs."); + assert(false && "same-tile mov without register mapping"); + } + } - func.walk([&](Operation *op) { - if (isa(op)) { - return; - } + template + void expandMovImpl(Operation *forwarder, const Topology &topo, + const DenseMap &reserve2phi) { + if (!validateForwarderShape(forwarder)) return; - llvm::json::Object op_obj; - op_obj["name"] = op->getName().getStringRef().str(); + // Basic info from forwarders. + Value source = forwarder->getOperand(0); + Operation *producer = source.getDefiningOp(); + auto links = getLinkChain(forwarder); + auto regs = getRegisterSteps(forwarder); + auto [producer_direction, consumer_direction] = computeDirections(links, topo); - // Result types. - llvm::json::Array result_types; - for (auto result : op->getResults()) { - std::string type_str; - llvm::raw_string_ostream os(type_str); - result.getType().print(os); - result_types.push_back(os.str()); - } - op_obj["result_types"] = std::move(result_types); - - // Operands. - llvm::json::Array operand_indices; - for (Value operand : op->getOperands()) { - if (auto defining_op = operand.getDefiningOp()) { - operand_indices.push_back( - defining_op->getName().getStringRef().str()); - } else { - operand_indices.push_back("block_arg"); + // Producer endpoints & intermediate hops. + setProducerDestination(producer, producer_direction, regs); + generateIntermediateHops(links, topo); + + // Gather consumers. + SmallVector, 2> consumers; + if constexpr (IsCtrl) { + consumers = collectCtrlMovConsumers(forwarder, reserve2phi); + if (consumers.empty()) return; + } else { + consumers = collectDataMovConsumers(forwarder); + } + + // Wire each consumer: prefer register rewiring; fallback to direction rewiring. + for (auto &[consOp, atVal] : consumers) { + if (!handleRegisterRewiring(consOp, atVal, regs, links, topo)) + handleDirectionRewiring(consOp, atVal, consumer_direction, links, forwarder); + } + } + + + // ---------- output generation ----------. + void logUnresolvedOperands() { + unsigned unsrc = 0, undst = 0; + for (auto &tile_entry : tile_time_instructions) { + std::pair tile_key = tile_entry.first; + int column = tile_key.first, row = tile_key.second; + for (auto ×tep_entry : tile_entry.second) { + int ts = timestep_entry.first; + std::vector &vec = timestep_entry.second; + for (size_t i = 0; i < vec.size(); ++i) { + Instruction &inst = vec[i]; + for (size_t si = 0; si < inst.src_operands.size(); ++si) { + Operand &s = inst.src_operands[si]; + if (s.operand == "UNRESOLVED") { + s.color = "ERROR"; ++unsrc; + llvm::errs() << "[UNRESOLVED SRC] tile("<(op)) { - auto val_attr = const_op.getValue(); - if (val_attr) { - if (auto int_attr = mlir::dyn_cast(val_attr)) { - op_obj["constant_value"] = std::to_string(int_attr.getInt()); - } else if (auto float_attr = mlir::dyn_cast(val_attr)) { - op_obj["constant_value"] = - std::to_string(float_attr.getValueAsDouble()); + inst.dst_operands.erase( + std::remove_if(inst.dst_operands.begin(), inst.dst_operands.end(), + [](const Operand &o){ return o.operand.empty() || o.operand=="UNKNOWN"; }), + inst.dst_operands.end()); + for (size_t di = 0; di < inst.dst_operands.size(); ++di) { + Operand &d = inst.dst_operands[di]; + if (d.operand == "UNRESOLVED") { + d.color = "ERROR"; ++undst; + llvm::errs() << "[UNRESOLVED DST] tile("<getAttrOfType("mapping_locs")) { - for (Attribute attr : attr_array) { - if (auto loc = mlir::dyn_cast(attr)) { - llvm::json::Object loc_obj; - if (auto idAttr = mlir::dyn_cast(loc.get("id"))) { - loc_obj["id"] = idAttr.getInt(); - } - if (auto resource_attr = - mlir::dyn_cast(loc.get("resource"))) { - loc_obj["resource"] = resource_attr.getValue().str(); - } - if (auto timestep_attr = - mlir::dyn_cast(loc.get("time_step"))) { - loc_obj["time_step"] = timestep_attr.getInt(); - } - loc_array.push_back(std::move(loc_obj)); - } + ArrayConfig buildArrayConfig(int columns, int rows) { + ArrayConfig config{columns, rows, {}}; + std::map, std::vector> tile_insts; + + // Flatten and sort by timesteps. + for (auto &[tile_key, timestep_map] : tile_time_instructions) { + auto &flat = tile_insts[tile_key]; + for (auto &[timestep, instruction_vec] : timestep_map) for (Instruction &inst : instruction_vec) flat.push_back(inst); + std::stable_sort(flat.begin(), flat.end(), + [](const Instruction &a, const Instruction &b){ return a.time_step < b.time_step; }); + } + + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < columns; ++c) { + auto it = tile_insts.find({c,r}); + if (it == tile_insts.end()) continue; + Tile tile(c, r, r*columns + c); + for (Instruction &inst : it->second) tile.entry.instructions.push_back(inst); + config.cores.push_back(std::move(tile)); + } + } + return config; + } + + void writeYAMLOutput(const ArrayConfig &config) { + std::error_code ec; + llvm::raw_fd_ostream yaml_out("tmp-generated-instructions.yaml", ec); + if (ec) return; + + yaml_out << "array_config:\n columns: " << config.columns << "\n rows: " << config.rows << "\n cores:\n"; + for (const Tile &core : config.cores) { + yaml_out << " - column: " << core.col_idx << "\n row: " << core.row_idx + << "\n core_id: \"" << core.core_id << "\"\n entries:\n"; + int entry_id = 0; + for (const Instruction &inst : core.entry.instructions) { + yaml_out << " - entry_id: \"entry" << entry_id++ << "\"\n instructions:\n" + << " - opcode: \"" << inst.opcode << "\"\n timestep: " << inst.time_step << "\n"; + // sources. + if (!inst.src_operands.empty()) { + yaml_out << " src_operands:\n"; + for (const Operand &opnd : inst.src_operands) + yaml_out << " - operand: \"" << opnd.operand << "\"\n color: \"" << opnd.color << "\"\n"; + } + // destinations. + if (!inst.dst_operands.empty()) { + yaml_out << " dst_operands:\n"; + for (const Operand &opnd : inst.dst_operands) + yaml_out << " - operand: \"" << opnd.operand << "\"\n color: \"" << opnd.color << "\"\n"; + } + } + } + yaml_out.close(); + } + + // Direction vs const/reg helpers. + static bool isDirectionalOperand(const std::string &operand) { + // Only non-directional operands start with $ (registers) or # (constants). + return !operand.empty() && operand[0] != '$' && operand[0] != '#'; + } + + static std::string formatOperand(const Operand &operand) { + std::string result = "[" + operand.operand; + if (isDirectionalOperand(operand.operand)) { + result += ", " + operand.color; + } + result += "]"; + return result; + } + + void writeASMOutput(const ArrayConfig &config) { + std::error_code ec; + llvm::raw_fd_ostream asm_out("tmp-generated-instructions.asm", ec); + if (ec) return; + + for (const Tile &core : config.cores) { + asm_out << "PE(" << core.col_idx << "," << core.row_idx << "):\n"; + for (const Instruction &inst : core.entry.instructions) { + asm_out << "{\n " << inst.opcode; + for (const Operand &operand : inst.src_operands) asm_out << ", " << formatOperand(operand); + if (!inst.dst_operands.empty()) { + asm_out << " -> "; + for (size_t i = 0; i < inst.dst_operands.size(); ++i) { + if (i > 0) asm_out << ", "; + asm_out << formatOperand(inst.dst_operands[i]); } } - op_obj["mapping_locs"] = std::move(loc_array); + asm_out << " (t=" << inst.time_step << ")\n}\n"; + } + asm_out << "\n"; + } + asm_out.close(); + } + + // Endpoint deposits: on destination tiles at earliest reg ts, move [incoming_dir] -> [$reg]. + // CTRL_MOV paths emit CTRL_MOV deposits; DATA_MOV paths emit DATA_MOV deposits. + void placeDstDeposit(const Topology &topo, int dstTileId, int ts, + StringRef incomingDir, int regId, bool asCtrlMov = false) { + uint64_t signature = (uint64_t)dstTileId << 32 ^ (uint64_t)ts << 16 ^ (uint64_t)regId; + if (!deposit_signatures.insert(signature).second) return; // already placed. + auto [tile_x, tile_y] = topo.tile_location.lookup(dstTileId); + Instruction inst(asCtrlMov ? "CTRL_MOV" : "DATA_MOV"); + inst.time_step = ts; + inst.src_operands.emplace_back(incomingDir.str(), "RED"); + inst.dst_operands.emplace_back("$" + std::to_string(regId), "RED"); + tile_time_instructions[{tile_x, tile_y}][ts].push_back(std::move(inst)); + } - op_array.push_back(std::move(op_obj)); - }); + // Utilities to access instruction buckets/pointers. + std::vector &getInstructionBucket(int column, int row, int time_step) { + return tile_time_instructions[{column,row}][time_step]; + } + Instruction* getInstructionPointer(Operation *operation) { + auto it = operation_to_instruction_reference.find(operation); + if (it == operation_to_instruction_reference.end()) return nullptr; + auto [c, r, t, idx] = it->second; + auto &vec = tile_time_instructions[{c,r}][t]; + if (idx < 0 || idx >= (int)vec.size()) return nullptr; + return &vec[idx]; + } - func_obj["operations"] = std::move(op_array); - functions_array.push_back(std::move(func_obj)); + // Replace the exact source slots in consumers that correspond to `value_at_consumer`, + // or fill the first UNRESOLVED placeholder if a 1:1 match wasn't found. + void setConsumerSourceExact(Operation *consumer, Value value_at_consumer, const std::string &text) { + Instruction *ci = getInstructionPointer(consumer); + if (!ci) return; + auto it = operation_to_operands.find(consumer); + if (it == operation_to_operands.end()) return; + auto &ops = it->second; + for (size_t i = 0; i < ops.size() && i < ci->src_operands.size(); ++i) { + if (ops[i] == value_at_consumer) { ci->src_operands[i].operand = text; return; } } + for (Operand &src : ci->src_operands) + if (src.operand == "UNRESOLVED") { src.operand = text; return; } + } - // Final JSON object. - llvm::json::Object root; - root["functions"] = std::move(functions_array); + // Appends a destination only once. + static void setUniqueDestination(Instruction *inst, const std::string &text){ + for (Operand &d : inst->dst_operands) if (d.operand == text) return; + inst->dst_operands.emplace_back(text, "RED"); + } - // llvm::outs() << llvm::formatv("{0:2}", - // llvm::json::Value(std::move(root))) << "\n"; - std::error_code ec; - llvm::raw_fd_ostream json_out("generated-instructions.json", ec); - if (ec) { - getOperation()->emitError( - "Failed to open 'generated-instructions.json' for writing: " + - ec.message()); - return signalPassFailure(); + // ---------- entry point ----------. + void runOnOperation() override { + ModuleOp module = getOperation(); + + for (auto func : module.getOps()) { + auto accel = func->getAttrOfType("accelerator"); + if (!accel || accel.getValue() != "neura") continue; + + auto [columns, rows] = getArrayDimensions(func); + Topology topo = getTopologyFromArchitecture(columns, rows); + + clearState(); + + // Single function-level walks: index + materialize + collect. + SmallVector dataMovs; + SmallVector ctrlMovs; + DenseMap reserve_to_phi_map; + indexIR(func, dataMovs, ctrlMovs, reserve_to_phi_map); + + // Expand forwarders without re-walking IR. + for (Operation *op : dataMovs) + expandMovImpl(op, topo, /*unused*/reserve_to_phi_map); + for (Operation *op : ctrlMovs) + expandMovImpl(op, topo, reserve_to_phi_map); + + // Debug unresolveds, then dump outputs. + logUnresolvedOperands(); + + ArrayConfig config = buildArrayConfig(columns, rows); + writeYAMLOutput(config); + writeASMOutput(config); } - json_out << llvm::formatv("{0:2}", llvm::json::Value(std::move(root))) - << "\n"; } }; -} // namespace +} // namespace. namespace mlir::neura { -std::unique_ptr createGenerateCodePass() { +std::unique_ptr createGenerateCodePass() { return std::make_unique(); } -} // namespace mlir::neura +} // namespace mlir::neura. diff --git a/test/code_gen/README.md b/test/code_gen/README.md new file mode 100644 index 00000000..140cbb34 --- /dev/null +++ b/test/code_gen/README.md @@ -0,0 +1,203 @@ +# Code Generation Test File Documentation + +## Overview + +This test file `test_code_generate.mlir` tests the final code generation process of a simple loop program. The program implements an accumulation loop from 0 to 9, adding 3.0 in each iteration. + +## Program Semantics + +Implements: +1. Initialize `i = 0`, `acc = 0.0` +2. While `i < 10` +3. `acc += 3.0`, `i += 1` +4. Return `acc` + +## YAML Format Description + +The generated YAML file describes the configuration and instruction scheduling of a 4x4 CGRA array: + +### Basic Structure +- `array_config`: Array configuration information + - `columns: 4`, `rows: 4`: 4x4 processor array + - `cores`: Detailed information for each processing core + +### Core Configuration +Each core contains: +- `column`, `row`: Position coordinates in the array +- `core_id`: Unique identifier for the core +- `entries`: List of execution contexts for this core + +### Instruction Format +Each `entry` contains: +- `entry_id` — the (single-instruction) context ID +- `instructions` — a list with one item: + - `opcode` — e.g. `CONSTANT`, `DATA_MOV`, `PHI`, `ICMP`, `FADD`, … + - `timestep` — the per-tile cycle at which the instruction executes + - `src_operands` — inputs + - `dst_operands` — outputs + +Operands encode: +- `#N` — immediate constant (e.g., `#10`) +- `$N` — local register (e.g., `$22`) +- `EAST | WEST | NORTH | SOUTH` — directional ports for inter-tile communication + +Each operand also has a `color` field. Colors are primarily meaningful for **directional** operands (to visualize/track routing), rather than register operands + +### YAML Example + +The following example shows the YAML structure for a few tiles with their instructions: + +```yaml +array_config: + columns: 4 + rows: 4 + cores: + - column: 0 + row: 0 + core_id: "0" + entries: + - entry_id: "entry0" + instructions: + - opcode: "CONSTANT" + timestep: 0 + src_operands: + - operand: "#0" + color: "RED" + dst_operands: + - operand: "EAST" + color: "RED" + - entry_id: "entry1" + instructions: + - opcode: "CONSTANT" + timestep: 1 + src_operands: + - operand: "#10" + color: "RED" + dst_operands: + - operand: "EAST" + color: "RED" + - column: 1 + row: 1 + core_id: "5" + entries: + - entry_id: "entry0" + instructions: + - opcode: "PHI" + timestep: 2 + src_operands: + - operand: "EAST" + color: "RED" + - operand: "SOUTH" + color: "RED" + dst_operands: + - operand: "EAST" + color: "RED" + - entry_id: "entry1" + instructions: + - opcode: "ICMP" + timestep: 4 + src_operands: + - operand: "EAST" + color: "RED" + - operand: "SOUTH" + color: "RED" + dst_operands: + - operand: "EAST" + color: "RED" + - operand: "NORTH" + color: "RED" + - operand: "$22" + color: "RED" + - operand: "$21" + color: "RED" + - operand: "SOUTH" + color: "RED" + - operand: "$20" + color: "RED" + - column: 2 + row: 1 + core_id: "6" + entries: + - entry_id: "entry0" + instructions: + - opcode: "ADD" + timestep: 3 + src_operands: + - operand: "WEST" + color: "RED" + - operand: "SOUTH" + color: "RED" + dst_operands: + - operand: "WEST" + color: "RED" + - operand: "$25" + color: "RED" + - entry_id: "entry1" + instructions: + - opcode: "FADD" + timestep: 5 + src_operands: + - operand: "$24" + color: "RED" + - operand: "NORTH" + color: "RED" + dst_operands: + - operand: "$26" + color: "RED" + - operand: "EAST" + color: "RED" +``` + +#### What Each Tile Does: + +**PE(0,0) - Constant Generation Tile:** +- `entry0` @t=0: Generates constant value 0 and sends it eastward +- `entry1` @t=1: Generates constant value 10 (loop upper bound) and sends it eastward + +**PE(1,1) - Control Flow Tile:** +- `entry0` @t=2: Merges data flows from east and south using PHI operation +- `entry1` @t=4: Performs integer comparison (i < 10), broadcasts result to multiple destinations including registers $22, $21, $20 + +**PE(2,1) - Arithmetic Tile:** +- `entry0` @t=3: Performs integer addition (i + 1) and stores result in register $25 +- `entry1` @t=5: Performs floating-point addition (accumulator + 3.0) and stores result in register $26 + + +## ASM Format Description +The ASM format is a human-readable assembly-style view per tile. + +### Basics +- **PE(x,y)** — the tile coordinates +- **Format** — `OPCODE, [src …] -> [dst …] (t=TIMESTEP)` +- **Operand tokens** + - `#N` — immediate (e.g., `#0`, `#10`, `#3.000000`) + - `$N` — local register (e.g., `$20`, `$22`, `$25`) + - `[operand]` — non-directional (reg/imm) + - `[DIRECTION, COLOR]` — directional with routing color + - e.g., `[EAST, RED]`, `[WEST, RED]`, `[NORTH, RED]`, `[SOUTH, RED]` + +## Timing Execution Examples +### PE(0,0) +- @t=0: `CONSTANT [#0] -> [EAST]` - Generate constant 0 and send to east +- @t=1: `CONSTANT [#10] -> [EAST]` - Generate constant 10 (loop upper bound) and send to east +- @t=4: `DATA_MOV [EAST] -> [NORTH]` - Receive data from east and forward to north + +### PE(1,1) +- @t=2: `PHI [EAST], [SOUTH] -> [EAST]` - Merge data flows from east and south +- @t=4: `ICMP [EAST], [SOUTH] -> [EAST], [NORTH], [$22], [$21], [SOUTH], [$20]` - Integer Compare operation, broadcasting results to multiple targets +- @t=5: `NOT [$20] -> [EAST]` - Negate the value in register $20 +- @t=6: `GRANT_PREDICATE [WEST], [$21] -> [EAST]` - Data authorization based on predicate conditions +- @t=7: `CTRL_MOV [WEST] -> [$20]` - Control flow movement, updating register $20 + +### PE(2,1) +- @t=3: `ADD [WEST], [SOUTH] -> [WEST], [$25]` - Execute addition operation +- @t=4: `PHI [$24], [EAST] -> [$24]` - Data flow merging +- @t=5: `FADD [$24], [NORTH] -> [$26], [EAST]` - Execute floating-point addition +- @t=6: `GRANT_PREDICATE [$25], [$24] -> [WEST]` - Data authorization based on conditions + +## Notes / Known Limitations + +### Current Implementation Constraints +- **One instruction per entry**: Multiple operations within a tile are currently emitted as separate entries to satisfy the simulator requirements. We will allow multiple instructions per entry in the future. +- **Default color scheme**: You'll typically see "RED" as the default virtual channel color in the YAML output +- **Entry-based scheduling**: Each execution context (entry) can only contain one instruction at a time diff --git a/test/code_gen/test_code_generate.mlir b/test/code_gen/test_code_generate.mlir new file mode 100644 index 00000000..37add24f --- /dev/null +++ b/test/code_gen/test_code_generate.mlir @@ -0,0 +1,701 @@ +// RUN: mlir-neura-opt %s \ +// RUN: --assign-accelerator \ +// RUN: --lower-llvm-to-neura \ +// RUN: --canonicalize-live-in \ +// RUN: --leverage-predicated-value \ +// RUN: --transform-ctrl-to-data-flow \ +// RUN: --insert-data-mov \ +// RUN: --map-to-accelerator="mapping-strategy=heuristic" \ +// RUN: --generate-code \ +// RUN: | FileCheck %s -check-prefix=MAPPING +// RUN: FileCheck %s --input-file=tmp-generated-instructions.yaml --check-prefix=YAML +// RUN: FileCheck %s --input-file=tmp-generated-instructions.asm --check-prefix=ASM + + +func.func @loop_test() -> f32 { + %n = llvm.mlir.constant(10 : i64) : i64 + %c0 = llvm.mlir.constant(0 : i64) : i64 + %c1 = llvm.mlir.constant(1 : i64) : i64 + %c1f = llvm.mlir.constant(3.0 : f32) : f32 + %acc_init = llvm.mlir.constant(0.0 : f32) : f32 + + llvm.br ^bb1(%c0, %acc_init : i64, f32) + +^bb1(%i: i64, %acc: f32): + %next_acc = llvm.fadd %acc, %c1f : f32 + %i_next = llvm.add %i, %c1 : i64 + %cmp = llvm.icmp "slt" %i_next, %n : i64 + llvm.cond_br %cmp, ^bb1(%i_next, %next_acc : i64, f32), ^exit(%next_acc : f32) + +^exit(%result: f32): + return %result : f32 +} + + // MAPPING: func.func @loop_test() -> f32 attributes {accelerator = "neura", mapping_info = {compiled_ii = 6 : i32, mapping_mode = "spatial-temporal", mapping_strategy = "heuristic", rec_mii = 4 : i32, res_mii = 2 : i32, x_tiles = 4 : i32, y_tiles = 4 : i32}} { + // MAPPING-NEXT: %0 = "neura.constant"() <{predicate = true, value = 10 : i64}> {mapping_locs = [{id = 0 : i32, resource = "tile", time_step = 1 : i32, x = 0 : i32, y = 0 : i32}]} : () -> !neura.data + // MAPPING-NEXT: %1 = "neura.data_mov"(%0) {mapping_locs = [{id = 0 : i32, resource = "link", time_step = 1 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %2 = "neura.grant_once"(%1) {mapping_locs = [{id = 1 : i32, resource = "tile", time_step = 2 : i32, x = 1 : i32, y = 0 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %3 = "neura.constant"() <{predicate = true, value = 0 : i64}> {mapping_locs = [{id = 0 : i32, resource = "tile", time_step = 0 : i32, x = 0 : i32, y = 0 : i32}]} : () -> !neura.data + // MAPPING-NEXT: %4 = "neura.data_mov"(%3) {mapping_locs = [{id = 0 : i32, resource = "link", time_step = 0 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %5 = "neura.grant_once"(%4) {mapping_locs = [{id = 1 : i32, resource = "tile", time_step = 1 : i32, x = 1 : i32, y = 0 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %6 = "neura.constant"() <{predicate = true, value = 1 : i64}> {mapping_locs = [{id = 2 : i32, resource = "tile", time_step = 0 : i32, x = 2 : i32, y = 0 : i32}]} : () -> !neura.data + // MAPPING-NEXT: %7 = "neura.data_mov"(%6) {mapping_locs = [{id = 8 : i32, resource = "register", time_step = 0 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %8 = "neura.grant_once"(%7) {mapping_locs = [{id = 2 : i32, resource = "tile", time_step = 1 : i32, x = 2 : i32, y = 0 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %9 = "neura.constant"() <{predicate = true, value = 3.000000e+00 : f32}> {mapping_locs = [{id = 14 : i32, resource = "tile", time_step = 2 : i32, x = 2 : i32, y = 3 : i32}]} : () -> !neura.data + // MAPPING-NEXT: %10 = "neura.data_mov"(%9) {mapping_locs = [{id = 45 : i32, resource = "link", time_step = 2 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %11 = "neura.grant_once"(%10) {mapping_locs = [{id = 10 : i32, resource = "tile", time_step = 3 : i32, x = 2 : i32, y = 2 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %12 = "neura.constant"() <{predicate = true, value = 0.000000e+00 : f32}> {mapping_locs = [{id = 11 : i32, resource = "tile", time_step = 2 : i32, x = 3 : i32, y = 2 : i32}]} : () -> !neura.data + // MAPPING-NEXT: %13 = "neura.data_mov"(%12) {mapping_locs = [{id = 36 : i32, resource = "link", time_step = 2 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %14 = "neura.grant_once"(%13) {mapping_locs = [{id = 7 : i32, resource = "tile", time_step = 3 : i32, x = 3 : i32, y = 1 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %15 = neura.reserve : !neura.data + // MAPPING-NEXT: %16 = "neura.data_mov"(%2) {mapping_locs = [{id = 4 : i32, resource = "register", time_step = 2 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %17 = "neura.phi"(%15, %16) {mapping_locs = [{id = 1 : i32, resource = "tile", time_step = 3 : i32, x = 1 : i32, y = 0 : i32}]} : (!neura.data, !neura.data) -> !neura.data + // MAPPING-NEXT: %18 = neura.reserve : !neura.data + // MAPPING-NEXT: %19 = "neura.data_mov"(%8) {mapping_locs = [{id = 8 : i32, resource = "register", time_step = 1 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %20 = "neura.phi"(%18, %19) {mapping_locs = [{id = 2 : i32, resource = "tile", time_step = 2 : i32, x = 2 : i32, y = 0 : i32}]} : (!neura.data, !neura.data) -> !neura.data + // MAPPING-NEXT: %21 = neura.reserve : !neura.data + // MAPPING-NEXT: %22 = "neura.data_mov"(%11) {mapping_locs = [{id = 40 : i32, resource = "register", time_step = 3 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %23 = "neura.phi"(%21, %22) {mapping_locs = [{id = 10 : i32, resource = "tile", time_step = 4 : i32, x = 2 : i32, y = 2 : i32}]} : (!neura.data, !neura.data) -> !neura.data + // MAPPING-NEXT: %24 = neura.reserve : !neura.data + // MAPPING-NEXT: %25 = "neura.data_mov"(%14) {mapping_locs = [{id = 21 : i32, resource = "link", time_step = 3 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %26 = "neura.phi"(%24, %25) {mapping_locs = [{id = 6 : i32, resource = "tile", time_step = 4 : i32, x = 2 : i32, y = 1 : i32}]} : (!neura.data, !neura.data) -> !neura.data + // MAPPING-NEXT: %27 = neura.reserve : !neura.data + // MAPPING-NEXT: %28 = "neura.data_mov"(%5) {mapping_locs = [{id = 4 : i32, resource = "link", time_step = 1 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %29 = "neura.phi"(%27, %28) {mapping_locs = [{id = 5 : i32, resource = "tile", time_step = 2 : i32, x = 1 : i32, y = 1 : i32}]} : (!neura.data, !neura.data) -> !neura.data + // MAPPING-NEXT: %30 = "neura.data_mov"(%26) {mapping_locs = [{id = 24 : i32, resource = "register", time_step = 4 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %31 = "neura.data_mov"(%23) {mapping_locs = [{id = 33 : i32, resource = "link", time_step = 4 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %32 = "neura.fadd"(%30, %31) {mapping_locs = [{id = 6 : i32, resource = "tile", time_step = 5 : i32, x = 2 : i32, y = 1 : i32}]} : (!neura.data, !neura.data) -> !neura.data + // MAPPING-NEXT: %33 = "neura.data_mov"(%29) {mapping_locs = [{id = 14 : i32, resource = "link", time_step = 2 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %34 = "neura.data_mov"(%20) {mapping_locs = [{id = 7 : i32, resource = "link", time_step = 2 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %35 = "neura.add"(%33, %34) {mapping_locs = [{id = 6 : i32, resource = "tile", time_step = 3 : i32, x = 2 : i32, y = 1 : i32}]} : (!neura.data, !neura.data) -> !neura.data + // MAPPING-NEXT: %36 = "neura.data_mov"(%35) {mapping_locs = [{id = 17 : i32, resource = "link", time_step = 3 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %37 = "neura.data_mov"(%17) {mapping_locs = [{id = 4 : i32, resource = "link", time_step = 3 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %38 = "neura.icmp"(%36, %37) <{cmpType = "slt"}> {mapping_locs = [{id = 5 : i32, resource = "tile", time_step = 4 : i32, x = 1 : i32, y = 1 : i32}]} : (!neura.data, !neura.data) -> !neura.data + // MAPPING-NEXT: %39 = "neura.data_mov"(%35) {mapping_locs = [{id = 25 : i32, resource = "register", time_step = 3 : i32}, {id = 25 : i32, resource = "register", time_step = 4 : i32}, {id = 25 : i32, resource = "register", time_step = 5 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %40 = "neura.data_mov"(%38) {mapping_locs = [{id = 14 : i32, resource = "link", time_step = 4 : i32}, {id = 24 : i32, resource = "register", time_step = 5 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %41 = neura.grant_predicate %39, %40 {mapping_locs = [{id = 6 : i32, resource = "tile", time_step = 6 : i32, x = 2 : i32, y = 1 : i32}]} : !neura.data, !neura.data -> !neura.data + // MAPPING-NEXT: neura.ctrl_mov %41 -> %27 {mapping_locs = [{id = 17 : i32, resource = "link", time_step = 6 : i32}, {id = 20 : i32, resource = "register", time_step = 7 : i32}]} : !neura.data !neura.data + // MAPPING-NEXT: %42 = "neura.data_mov"(%32) {mapping_locs = [{id = 26 : i32, resource = "register", time_step = 5 : i32}, {id = 26 : i32, resource = "register", time_step = 6 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %43 = "neura.data_mov"(%38) {mapping_locs = [{id = 16 : i32, resource = "link", time_step = 4 : i32}, {id = 28 : i32, resource = "link", time_step = 5 : i32}, {id = 33 : i32, resource = "link", time_step = 6 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %44 = neura.grant_predicate %42, %43 {mapping_locs = [{id = 6 : i32, resource = "tile", time_step = 7 : i32, x = 2 : i32, y = 1 : i32}]} : !neura.data, !neura.data -> !neura.data + // MAPPING-NEXT: neura.ctrl_mov %44 -> %24 {mapping_locs = [{id = 24 : i32, resource = "register", time_step = 7 : i32}, {id = 24 : i32, resource = "register", time_step = 8 : i32}, {id = 24 : i32, resource = "register", time_step = 9 : i32}]} : !neura.data !neura.data + // MAPPING-NEXT: %45 = "neura.data_mov"(%23) {mapping_locs = [{id = 31 : i32, resource = "link", time_step = 4 : i32}, {id = 29 : i32, resource = "link", time_step = 5 : i32}, {id = 20 : i32, resource = "register", time_step = 6 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %46 = "neura.data_mov"(%38) {mapping_locs = [{id = 22 : i32, resource = "register", time_step = 4 : i32}, {id = 22 : i32, resource = "register", time_step = 5 : i32}, {id = 22 : i32, resource = "register", time_step = 6 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %47 = neura.grant_predicate %45, %46 {mapping_locs = [{id = 5 : i32, resource = "tile", time_step = 7 : i32, x = 1 : i32, y = 1 : i32}]} : !neura.data, !neura.data -> !neura.data + // MAPPING-NEXT: neura.ctrl_mov %47 -> %21 {mapping_locs = [{id = 14 : i32, resource = "link", time_step = 7 : i32}, {id = 20 : i32, resource = "link", time_step = 8 : i32}, {id = 41 : i32, resource = "register", time_step = 9 : i32}]} : !neura.data !neura.data + // MAPPING-NEXT: %48 = "neura.data_mov"(%20) {mapping_locs = [{id = 5 : i32, resource = "link", time_step = 2 : i32}, {id = 2 : i32, resource = "link", time_step = 3 : i32}, {id = 1 : i32, resource = "link", time_step = 4 : i32}, {id = 10 : i32, resource = "link", time_step = 5 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %49 = "neura.data_mov"(%38) {mapping_locs = [{id = 21 : i32, resource = "register", time_step = 4 : i32}, {id = 21 : i32, resource = "register", time_step = 5 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %50 = neura.grant_predicate %48, %49 {mapping_locs = [{id = 5 : i32, resource = "tile", time_step = 6 : i32, x = 1 : i32, y = 1 : i32}]} : !neura.data, !neura.data -> !neura.data + // MAPPING-NEXT: neura.ctrl_mov %50 -> %18 {mapping_locs = [{id = 14 : i32, resource = "link", time_step = 6 : i32}, {id = 19 : i32, resource = "link", time_step = 7 : i32}]} : !neura.data !neura.data + // MAPPING-NEXT: %51 = "neura.data_mov"(%17) {mapping_locs = [{id = 4 : i32, resource = "register", time_step = 3 : i32}, {id = 4 : i32, resource = "register", time_step = 4 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %52 = "neura.data_mov"(%38) {mapping_locs = [{id = 15 : i32, resource = "link", time_step = 4 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %53 = neura.grant_predicate %51, %52 {mapping_locs = [{id = 1 : i32, resource = "tile", time_step = 5 : i32, x = 1 : i32, y = 0 : i32}]} : !neura.data, !neura.data -> !neura.data + // MAPPING-NEXT: neura.ctrl_mov %53 -> %15 {mapping_locs = [{id = 5 : i32, resource = "register", time_step = 5 : i32}, {id = 5 : i32, resource = "register", time_step = 6 : i32}, {id = 5 : i32, resource = "register", time_step = 7 : i32}, {id = 5 : i32, resource = "register", time_step = 8 : i32}]} : !neura.data !neura.data + // MAPPING-NEXT: %54 = "neura.data_mov"(%38) {mapping_locs = [{id = 20 : i32, resource = "register", time_step = 4 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %55 = "neura.not"(%54) {mapping_locs = [{id = 5 : i32, resource = "tile", time_step = 5 : i32, x = 1 : i32, y = 1 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %56 = "neura.data_mov"(%32) {mapping_locs = [{id = 18 : i32, resource = "link", time_step = 5 : i32}, {id = 28 : i32, resource = "register", time_step = 6 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %57 = "neura.data_mov"(%55) {mapping_locs = [{id = 14 : i32, resource = "link", time_step = 5 : i32}, {id = 18 : i32, resource = "link", time_step = 6 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: %58 = neura.grant_predicate %56, %57 {mapping_locs = [{id = 7 : i32, resource = "tile", time_step = 7 : i32, x = 3 : i32, y = 1 : i32}]} : !neura.data, !neura.data -> !neura.data + // MAPPING-NEXT: %59 = "neura.data_mov"(%58) {mapping_locs = [{id = 22 : i32, resource = "link", time_step = 7 : i32}]} : (!neura.data) -> !neura.data + // MAPPING-NEXT: "neura.return"(%59) {mapping_locs = [{id = 3 : i32, resource = "tile", time_step = 8 : i32, x = 3 : i32, y = 0 : i32}]} : (!neura.data) -> () + // MAPPING-NEXT: } + +// Each core represents a processing element in the CGRA array +// Example: column: 1, row: 1 represents the core at position (1,1) in the 4x4 grid +// Each core contains multiple entries (execution contexts) with instructions +// Instructions are organized by timestep and include source/destination operands +// Tile (1,1) : per-cycle schedule and routing summary. +// +// entry0 @ t=2: +// PHI merges tokens arriving from EAST and SOUTH, then forwards the selected +// value out to EAST. +// +// entry1 @ t=4: +// ICMP consumes EAST and SOUTH, then BROADCASTS its result: +// - to EAST / NORTH / SOUTH (for downstream tiles), +// - and into local registers $22, $21, $20 (to retain the value for later use). +// +// entry2 @ t=5: +// NOT reads temporary $20 and forwards the negated value to EAST. +// +// entry3 @ t=6: +// GRANT_PREDICATE uses a control input from WEST together with predicate +// state latched in $21, and forwards the grant out to EAST. +// +// entry4 @ t=6: +// DATA_MOV performs a register deposit: value arriving from SOUTH is written +// into local register $20. +// +// entry5 @ t=7: +// GRANT_PREDICATE combines the recently updated $20 with $22 to produce a +// new grant and forwards it to EAST. +// +// entry6 @ t=7: +// CTRL_MOV performs a control deposit: control token arriving from WEST is +// written into local register $20. +// + +// YAML: array_config: +// YAML-NEXT: columns: 4 +// YAML-NEXT: rows: 4 +// YAML-NEXT: cores: +// YAML-NEXT: - column: 0 +// YAML-NEXT: row: 0 +// YAML-NEXT: core_id: "0" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "CONSTANT" +// YAML-NEXT: timestep: 0 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "#0" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry1" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "CONSTANT" +// YAML-NEXT: timestep: 1 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "#10" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry2" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 4 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 1 +// YAML-NEXT: row: 0 +// YAML-NEXT: core_id: "1" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_ONCE" +// YAML-NEXT: timestep: 1 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry1" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_ONCE" +// YAML-NEXT: timestep: 2 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$4" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry2" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "PHI" +// YAML-NEXT: timestep: 3 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$5" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$4" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$4" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry3" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 3 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry4" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_PREDICATE" +// YAML-NEXT: timestep: 5 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$4" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$5" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 2 +// YAML-NEXT: row: 0 +// YAML-NEXT: core_id: "2" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "CONSTANT" +// YAML-NEXT: timestep: 0 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "#1" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$8" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry1" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_ONCE" +// YAML-NEXT: timestep: 1 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$8" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$8" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry2" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "PHI" +// YAML-NEXT: timestep: 2 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$8" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 3 +// YAML-NEXT: row: 0 +// YAML-NEXT: core_id: "3" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "RETURN" +// YAML-NEXT: timestep: 8 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 0 +// YAML-NEXT: row: 1 +// YAML-NEXT: core_id: "4" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 5 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 1 +// YAML-NEXT: row: 1 +// YAML-NEXT: core_id: "5" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "PHI" +// YAML-NEXT: timestep: 2 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry1" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "ICMP" +// YAML-NEXT: timestep: 4 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$22" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$21" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$20" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry2" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "NOT" +// YAML-NEXT: timestep: 5 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$20" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry3" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_PREDICATE" +// YAML-NEXT: timestep: 6 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$21" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry4" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 6 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$20" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry5" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_PREDICATE" +// YAML-NEXT: timestep: 7 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$20" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$22" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry6" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "CTRL_MOV" +// YAML-NEXT: timestep: 7 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$20" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 2 +// YAML-NEXT: row: 1 +// YAML-NEXT: core_id: "6" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "ADD" +// YAML-NEXT: timestep: 3 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$25" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry1" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "PHI" +// YAML-NEXT: timestep: 4 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$24" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$24" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry2" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "FADD" +// YAML-NEXT: timestep: 5 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$24" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$26" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry3" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 5 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$24" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry4" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_PREDICATE" +// YAML-NEXT: timestep: 6 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$25" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$24" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry5" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 6 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry6" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_PREDICATE" +// YAML-NEXT: timestep: 7 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$26" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$24" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry7" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "CTRL_MOV" +// YAML-NEXT: timestep: 7 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry8" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "CTRL_MOV" +// YAML-NEXT: timestep: 8 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 3 +// YAML-NEXT: row: 1 +// YAML-NEXT: core_id: "7" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_ONCE" +// YAML-NEXT: timestep: 3 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry1" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 6 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$28" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry2" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_PREDICATE" +// YAML-NEXT: timestep: 7 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "$28" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 1 +// YAML-NEXT: row: 2 +// YAML-NEXT: core_id: "9" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 5 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry1" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 5 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 2 +// YAML-NEXT: row: 2 +// YAML-NEXT: core_id: "10" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_ONCE" +// YAML-NEXT: timestep: 3 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$40" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry1" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "PHI" +// YAML-NEXT: timestep: 4 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "$40" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry2" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "DATA_MOV" +// YAML-NEXT: timestep: 6 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "WEST" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - entry_id: "entry3" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "CTRL_MOV" +// YAML-NEXT: timestep: 9 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "NORTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "$41" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 3 +// YAML-NEXT: row: 2 +// YAML-NEXT: core_id: "11" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "CONSTANT" +// YAML-NEXT: timestep: 2 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "#0.000000" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" +// YAML-NEXT: - column: 2 +// YAML-NEXT: row: 3 +// YAML-NEXT: core_id: "14" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "CONSTANT" +// YAML-NEXT: timestep: 2 +// YAML-NEXT: src_operands: +// YAML-NEXT: - operand: "#3.000000" +// YAML-NEXT: color: "RED" +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "SOUTH" +// YAML-NEXT: color: "RED" + +// ASM-LABEL: PE(0,0): +// ASM: CONSTANT, [#0] -> [EAST, RED] (t=0) +// ASM: CONSTANT, [#10] -> [EAST, RED] (t=1) +// ASM: DATA_MOV, [EAST, RED] -> [NORTH, RED] (t=4) + +// ASM-LABEL: PE(1,0): +// ASM: GRANT_ONCE, [WEST, RED] -> [NORTH, RED] (t=1) +// ASM: GRANT_ONCE, [WEST, RED] -> [$4] (t=2) +// ASM: PHI, [$5], [$4] -> [NORTH, RED], [$4] (t=3) +// ASM: DATA_MOV, [EAST, RED] -> [WEST, RED] (t=3) +// ASM: GRANT_PREDICATE, [$4], [NORTH, RED] -> [$5] (t=5) + +// ASM-LABEL: PE(2,0): +// ASM: CONSTANT, [#1] -> [$8] (t=0) +// ASM: GRANT_ONCE, [$8] -> [$8] (t=1) +// ASM: PHI, [NORTH, RED], [$8] -> [NORTH, RED], [WEST, RED] (t=2) + +// ASM-LABEL: PE(3,0): +// ASM: RETURN, [NORTH, RED] (t=8) + +// ASM-LABEL: PE(0,1): +// ASM: DATA_MOV, [SOUTH, RED] -> [EAST, RED] (t=5) + +// ASM-LABEL: PE(1,1): +// ASM: PHI, [EAST, RED], [SOUTH, RED] -> [EAST, RED] (t=2) +// ASM: ICMP, [EAST, RED], [SOUTH, RED] -> [EAST, RED], [NORTH, RED], [$22], [$21], [SOUTH, RED], [$20] (t=4) +// ASM: NOT, [$20] -> [EAST, RED] (t=5) +// ASM: GRANT_PREDICATE, [WEST, RED], [$21] -> [EAST, RED] (t=6) +// ASM: DATA_MOV, [SOUTH, RED] -> [$20] (t=6) +// ASM: GRANT_PREDICATE, [$20], [$22] -> [EAST, RED] (t=7) +// ASM: CTRL_MOV, [WEST, RED] -> [$20] (t=7) + +// ASM-LABEL: PE(2,1): +// ASM: ADD, [WEST, RED], [SOUTH, RED] -> [WEST, RED], [$25] (t=3) +// ASM: PHI, [$24], [EAST, RED] -> [$24] (t=4) +// ASM: FADD, [$24], [NORTH, RED] -> [$26], [EAST, RED] (t=5) +// ASM: DATA_MOV, [EAST, RED] -> [$24] (t=5) +// ASM: GRANT_PREDICATE, [$25], [$24] -> [WEST, RED] (t=6) +// ASM: DATA_MOV, [WEST, RED] -> [EAST, RED] (t=6) +// ASM: GRANT_PREDICATE, [$26], [NORTH, RED] -> [$24] (t=7) +// ASM: CTRL_MOV, [WEST, RED] -> [SOUTH, RED] (t=7) +// ASM: CTRL_MOV, [WEST, RED] -> [NORTH, RED] (t=8) + +// ASM-LABEL: PE(3,1): +// ASM: GRANT_ONCE, [NORTH, RED] -> [WEST, RED] (t=3) +// ASM: DATA_MOV, [EAST, RED] -> [$28] (t=6) +// ASM: GRANT_PREDICATE, [$28], [WEST, RED] -> [SOUTH, RED] (t=7) + +// ASM-LABEL: PE(1,2): +// ASM: DATA_MOV, [SOUTH, RED] -> [EAST, RED] (t=5) +// ASM: DATA_MOV, [EAST, RED] -> [SOUTH, RED] (t=5) + +// ASM-LABEL: PE(2,2): +// ASM: GRANT_ONCE, [NORTH, RED] -> [$40] (t=3) +// ASM: PHI, [SOUTH, RED], [$40] -> [SOUTH, RED], [WEST, RED] (t=4) +// ASM: DATA_MOV, [WEST, RED] -> [SOUTH, RED] (t=6) +// ASM: CTRL_MOV, [NORTH, RED] -> [$41] (t=9) + +// ASM-LABEL: PE(3,2): +// ASM: CONSTANT, [#0.000000] -> [SOUTH, RED] (t=2) + +// ASM-LABEL: PE(2,3): +// ASM: CONSTANT, [#3.000000] -> [SOUTH, RED] (t=2) + + + diff --git a/test/mapping_quality/branch_for.mlir b/test/mapping_quality/branch_for.mlir index 194cc6c9..421f85c1 100644 --- a/test/mapping_quality/branch_for.mlir +++ b/test/mapping_quality/branch_for.mlir @@ -58,7 +58,8 @@ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized" \ // RUN: --generate-code -// RUN: FileCheck %s --input-file=generated-instructions.json -check-prefix=INST +// RUN: FileCheck %s --input-file=tmp-generated-instructions.yaml -check-prefix=YAML +// RUN: FileCheck %s --input-file=tmp-generated-instructions.asm --check-prefix=ASM func.func @loop_test() -> f32 { %n = llvm.mlir.constant(10 : i64) : i64 @@ -302,11 +303,30 @@ func.func @loop_test() -> f32 { // MAPPING-NEXT: "neura.return"(%49) {mapping_locs = [{id = 13 : i32, resource = "tile", time_step = 7 : i32, x = 1 : i32, y = 3 : i32}]} : (!neura.data) -> () // MAPPING-NEXT: } -// INST: "name": "neura.fadd", -// INST-NEXT: "operands": [ -// INST-NEXT: "neura.data_mov", -// INST-NEXT: "neura.data_mov" -// INST-NEXT: ], -// INST-NEXT: "result_types": [ -// INST-NEXT: "!neura.data" -// INST-NEXT: ] \ No newline at end of file +// YAML: array_config: +// YAML-NEXT: columns: 4 +// YAML-NEXT: rows: 4 +// YAML-NEXT: cores: +// YAML-NEXT: - column: 0 +// YAML-NEXT: row: 1 +// YAML-NEXT: core_id: "4" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_ONCE" +// YAML-NEXT: timestep: 2 +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" + + +// ASM-LABEL: PE(0,1): +// ASM: GRANT_ONCE -> [EAST, RED] (t=2) + +// ASM-LABEL: PE(1,1): +// ASM: DATA_MOV, [NORTH, RED] -> [EAST, RED] (t=2) +// ASM: PHI, [$20], [WEST, RED] -> [NORTH, RED], [$20] (t=3) +// ASM: DATA_MOV, [NORTH, RED] -> [EAST, RED] (t=3) +// ASM: DATA_MOV, [SOUTH, RED] -> [$21] (t=5) +// ASM: CTRL_MOV, [EAST, RED] -> [NORTH, RED] (t=5) +// ASM: GRANT_PREDICATE, [$20], [$21] -> [$20] (t=6) \ No newline at end of file diff --git a/test/neura/ctrl/branch_for.mlir b/test/neura/ctrl/branch_for.mlir index 976b9f37..a92492a6 100644 --- a/test/neura/ctrl/branch_for.mlir +++ b/test/neura/ctrl/branch_for.mlir @@ -58,7 +58,8 @@ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=simple" \ // RUN: --generate-code -// RUN: FileCheck %s --input-file=generated-instructions.json -check-prefix=INST +// RUN: FileCheck %s --input-file=tmp-generated-instructions.yaml -check-prefix=YAML +// RUN: FileCheck %s --input-file=tmp-generated-instructions.asm --check-prefix=ASM func.func @loop_test() -> f32 { %n = llvm.mlir.constant(10 : i64) : i64 @@ -302,11 +303,30 @@ func.func @loop_test() -> f32 { // MAPPING-NEXT: "neura.return"(%49) {mapping_locs = [{id = 13 : i32, resource = "tile", time_step = 7 : i32, x = 1 : i32, y = 3 : i32}]} : (!neura.data) -> () // MAPPING-NEXT: } -// INST: "name": "neura.fadd", -// INST-NEXT: "operands": [ -// INST-NEXT: "neura.data_mov", -// INST-NEXT: "neura.data_mov" -// INST-NEXT: ], -// INST-NEXT: "result_types": [ -// INST-NEXT: "!neura.data" -// INST-NEXT: ] \ No newline at end of file +// YAML: array_config: +// YAML-NEXT: columns: 4 +// YAML-NEXT: rows: 4 +// YAML-NEXT: cores: +// YAML-NEXT: - column: 0 +// YAML-NEXT: row: 1 +// YAML-NEXT: core_id: "4" +// YAML-NEXT: entries: +// YAML-NEXT: - entry_id: "entry0" +// YAML-NEXT: instructions: +// YAML-NEXT: - opcode: "GRANT_ONCE" +// YAML-NEXT: timestep: 2 +// YAML-NEXT: dst_operands: +// YAML-NEXT: - operand: "EAST" +// YAML-NEXT: color: "RED" + + +// ASM-LABEL: PE(0,1): +// ASM: GRANT_ONCE -> [EAST, RED] (t=2) + +// ASM-LABEL: PE(1,1): +// ASM: DATA_MOV, [NORTH, RED] -> [EAST, RED] (t=2) +// ASM: PHI, [$20], [WEST, RED] -> [NORTH, RED], [$20] (t=3) +// ASM: DATA_MOV, [NORTH, RED] -> [EAST, RED] (t=3) +// ASM: DATA_MOV, [SOUTH, RED] -> [$21] (t=5) +// ASM: CTRL_MOV, [EAST, RED] -> [NORTH, RED] (t=5) +// ASM: GRANT_PREDICATE, [$20], [$21] -> [$20] (t=6) \ No newline at end of file