Repository: tenstorrent/tt-metal
Pull request #20320: Implement new op, padded_slice
Author: login: sankarmanoj-tt
Created: 2025-04-08T04:03:16Z | Merged: 2025-05-23T11:07:08Z

### Ticket
#19062 

### Problem description
Some ops like Conv2d expect their inputs to be sharded. If the input/shard dimensions are not a multiple of 32, then the op would pad those dimensions of the input. 

To improve performance for ops that use slice, we would like to merge slice + padding + sharding into a single op. 

### What's changed
Implemented a new op, padded_slice that takes an interleaved input, slices it, applies padding, and writes to sharded L1.
Takes an output memory config as an argument. The shard size in this memory config determines the padding. 

### Checklist
- [x] [All post commit](https://github.com/tenstorrent/tt-metal/actions/workflows/all-post-commit-workflows.yaml) CI [passes](https://github.com/tenstorrent/tt-metal/actions/runs/15190520446)
- [x] [Blackhole Post commit](https://github.com/tenstorrent/tt-metal/actions/workflows/blackhole-post-commit.yaml) CI [passes](https://github.com/tenstorrent/tt-metal/actions/runs/15187029976)
- [x] [Model regression](https://github.com/tenstorrent/tt-metal/actions/workflows/perf-models.yaml) CI [passes](https://github.com/tenstorrent/tt-metal/actions/runs/15190554929) (if applicable)
- [ ] New/Existing tests provide coverage for changes


Diff:
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 54e35776eae7..582ffd354a44 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -102,6 +102,7 @@ ttnn/cpp/ttnn/operations/experimental/conv3d/ @tenstorrent/metalium-developers-c
 ttnn/cpp/ttnn/operations/experimental/cnn/ @tenstorrent/metalium-developers-convolutions @esmalTT
 ttnn/cpp/ttnn/operations/experimental/matmul/ @TT-BrianLiu @bbradelTT @yugaoTT @vsureshTT @edwinleeTT @nsorabaTT
 ttnn/cpp/ttnn/operations/experimental/slice_write/ @tenstorrent/metalium-developers-convolutions @sjameelTT @ntarafdar @amorrisonTT
+ttnn/cpp/ttnn/operations/experimental/padded_slice/ @tenstorrent/metalium-developers-convolutions @sjameelTT @ntarafdar @amorrisonTT
 ttnn/cpp/ttnn/operations/experimental/reduction/ @bbradelTT @sjameelTT @vsureshTT @edwinleeTT @nsorabaTT @aczajkowskiTT
 ttnn/cpp/ttnn/operations/eltwise/ @patrickroberts @sjameelTT @ntarafdar @dchenTT
 ttnn/cpp/ttnn/operations/eltwise/quantization/ @wenbinlyuTT @patrickroberts @sjameelTT
diff --git a/tests/ttnn/unit_tests/operations/test_slice.py b/tests/ttnn/unit_tests/operations/test_slice.py
index 18ede235d6de..21d99897f83c 100644
--- a/tests/ttnn/unit_tests/operations/test_slice.py
+++ b/tests/ttnn/unit_tests/operations/test_slice.py
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+# SPDX-FileCopyrightText: © 2023 Tenstorrent AI ULC
 
 # SPDX-License-Identifier: Apache-2.0
 
@@ -9,6 +9,8 @@
 import ttnn
 from models.utility_functions import is_grayskull
 from tests.ttnn.utils_for_testing import assert_with_pcc
+from .test_utils import round_up
+import math
 
 
 def run_slice_rm_sharded(device, n, c, h, w):
@@ -1077,3 +1079,103 @@ def test_ttnn_slice_whisper(
 
         ttnn_output = ttnn.to_torch(ttnn_output)
         assert_with_pcc(torch_output, ttnn_output, 0.999)
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, cores",
+    [
+        [[2, 256, 256, 56], 128, 64],
+        [[2, 256, 128, 23], 16, 8],
+        [[2, 256, 256, 37], 64, 64],
+        [[2, 8, 8, 32], 2, 4],
+        [[2, 8, 16, 2], 2, 8],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_height_sharded_for_conv2d(device, dims, slice_dim, slice_size, cores, layout, orientation):
+    core_grid = device.compute_with_storage_grid_size()
+    if core_grid.x * core_grid.y < cores:
+        pytest.skip(
+            "Skipping test_slice_height_sharded_for_conv2d as device does not have enough Tensix cores. Needs %d, but device has %d"
+            % (cores, core_grid.x * core_grid.y)
+        )
+
+    strides = [1, 1, 1, 1]
+    torch.manual_seed(2005)
+    torch_input = torch.randint(-10, 10, dims).to(dtype=torch.bfloat16)
+    core_range = ttnn.num_cores_to_corerangeset(cores, core_grid, orientation == ttnn.ShardOrientation.ROW_MAJOR)
+    num_slices = dims[slice_dim] // slice_size
+    ttnn_input = ttnn.from_torch(
+        torch_input, device=device, layout=layout, dtype=ttnn.bfloat16, memory_config=ttnn.DRAM_MEMORY_CONFIG
+    )
+    parallel_config = ttnn.SlidingWindowParallelConfig(
+        grid=core_range, shard_scheme=ttnn.TensorMemoryLayout.HEIGHT_SHARDED, shard_orientation=orientation
+    )
+    padded_channels = round_up(dims[-1], 32)
+    padded_torch_input = torch.nn.functional.pad(torch_input, (0, padded_channels - dims[-1]))
+    for i in range(num_slices):
+        begins = [0, 0, 0, 0]
+        ends = [dims[0], dims[1], dims[2], dims[3]]
+        begins[slice_dim] = i * slice_size
+        ends[slice_dim] = (i + 1) * slice_size
+        this_torch_output = padded_torch_input[begins[0] : ends[0], begins[1] : ends[1], begins[2] : ends[2]]
+        output_shape = this_torch_output.shape
+        output_shape = [1, 1, output_shape[0] * output_shape[1] * output_shape[2], round_up(output_shape[3], 32)]
+
+        memory_config = ttnn._ttnn.operations.conv.create_sharded_memory_config_from_parallel_config(
+            output_shape, parallel_config, 1
+        )
+        this_ttnn_output = ttnn.padded_slice(ttnn_input, begins, ends, strides, memory_config=memory_config)
+        output = ttnn.to_torch(this_ttnn_output)
+        output = torch.reshape(output, this_torch_output.shape)
+        assert torch.allclose(this_torch_output, output, atol=1e-2, rtol=1e-2)
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, core_x, core_y",
+    [
+        [[2, 64, 64, 256], 32, 4, 4],
+        [[2, 64, 64, 512], 16, 4, 4],
+        [[2, 16, 16, 1024], 4, 4, 4],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_block_sharded_for_conv2d(device, dims, slice_dim, slice_size, core_x, core_y, layout, orientation):
+    core_grid = device.core_grid
+    if core_grid.x < core_x or core_grid.y < core_y:
+        pytest.skip(
+            "Skipping test_slice_block_sharded_for_conv2d as device does not have enough Tensix cores. Needs %s, but device has %s"
+            % ((core_x, core_y), (core_grid.x, core_grid.y))
+        )
+
+    strides = [1, 1, 1, 1]
+    torch.manual_seed(2005)
+    torch_input = torch.randint(-10, 10, dims).to(dtype=torch.bfloat16)
+    num_slices = dims[slice_dim] // slice_size
+    ttnn_input = ttnn.from_torch(
+        torch_input, device=device, layout=layout, dtype=ttnn.bfloat16, memory_config=ttnn.DRAM_MEMORY_CONFIG
+    )
+
+    padded_channels = round_up(dims[-1], 32)
+    padded_torch_input = torch.nn.functional.pad(torch_input, (0, padded_channels - dims[-1]))
+    core_grid = ttnn.CoreGrid(x=core_x, y=core_y)
+
+    for i in range(num_slices):
+        begins = [0, 0, 0, 0]
+        ends = [dims[0], dims[1], dims[2], dims[3]]
+        begins[slice_dim] = i * slice_size
+        ends[slice_dim] = (i + 1) * slice_size
+        this_torch_output = padded_torch_input[begins[0] : ends[0], begins[1] : ends[1], begins[2] : ends[2]]
+        output_shape = this_torch_output.shape
+        output_shape = [1, 1, output_shape[0] * output_shape[1] * output_shape[2], round_up(output_shape[3], 32)]
+        memory_config = ttnn.create_sharded_memory_config_(
+            output_shape, core_grid, ttnn.ShardStrategy.BLOCK, orientation
+        )
+        this_ttnn_output = ttnn.padded_slice(ttnn_input, begins, ends, strides, memory_config=memory_config)
+        output = ttnn.to_torch(this_ttnn_output)
+        output = torch.reshape(output, this_torch_output.shape)
+        assert torch.allclose(this_torch_output, output, atol=1e-2, rtol=1e-2)
diff --git a/tests/ttnn/unit_tests/operations/test_utils.py b/tests/ttnn/unit_tests/operations/test_utils.py
index 23634fe584d2..8eee53d48f71 100644
--- a/tests/ttnn/unit_tests/operations/test_utils.py
+++ b/tests/ttnn/unit_tests/operations/test_utils.py
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: © 2024 Tenstorrent Inc.
+# SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
 
 # SPDX-License-Identifier: Apache-2.0
 
@@ -6,7 +6,7 @@
 
 import pytest
 import torch
-
+import math
 import ttnn
 
 TILE_HEIGHT = 32
@@ -17,6 +17,13 @@
 compute_kernel_ids = ["fp32_dest_acc_en=False", "fp32_dest_acc_en=True"]
 
 
+def round_up(a, b):
+    """
+    Round up a to the nearest multiple of b
+    """
+    return b * math.ceil(a / b)
+
+
 def get_compute_kernel_options(fp32_dest_acc_en):
     if fp32_dest_acc_en is None:
         return None
diff --git a/ttnn/CMakeLists.txt b/ttnn/CMakeLists.txt
index 13a767951a5b..de85dd888d8d 100644
--- a/ttnn/CMakeLists.txt
+++ b/ttnn/CMakeLists.txt
@@ -485,6 +485,9 @@ set(TTNN_OP_SRCS
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/transformer/nlp_kv_cache_load_slice/device/nlp_kv_cache_load_slice_program_factory.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/transformer/nlp_kv_cache_load_slice/device/nlp_kv_cache_load_slice_device_operation.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/transformer/nlp_kv_cache_load_slice/nlp_kv_cache_load_slice.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/padded_slice/padded_slice.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/slice_write/device/slice_write_op.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/slice_write/device/slice_write_program_factory.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/slice_write/slice_write.cpp
@@ -920,6 +923,7 @@ set(TTNN_SRC_PYBIND
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/reduction/cumsum/cumsum_${PY_BINDING}.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/reshape/view_${PY_BINDING}.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/slice_write/slice_write_${PY_BINDING}.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/padded_slice/padded_slice_${PY_BINDING}.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/ssm/hc_sum_reduce/hc_sum_reduce_${PY_BINDING}.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/ssm/prefix_scan/prefix_scan_${PY_BINDING}.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/cpp/ttnn/operations/experimental/ssm/repeat_and_interleave_eltwise_mul/repeat_and_interleave_eltwise_mul_${PY_BINDING}.cpp
diff --git a/ttnn/cpp/ttnn/operations/experimental/experimental_pybind.cpp b/ttnn/cpp/ttnn/operations/experimental/experimental_pybind.cpp
index 16579fd63a24..18e7dbfd18fa 100644
--- a/ttnn/cpp/ttnn/operations/experimental/experimental_pybind.cpp
+++ b/ttnn/cpp/ttnn/operations/experimental/experimental_pybind.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+// SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
 //
 // SPDX-License-Identifier: Apache-2.0
 
@@ -41,18 +41,19 @@
 #include "ttnn/operations/experimental/plusone/plusone_pybind.hpp"
 #include "ttnn/operations/experimental/dropout/dropout_pybind.hpp"
 #include "ttnn/operations/experimental/bcast_to/bcast_to_pybind.hpp"
-
 #include "ttnn/operations/experimental/reshape/view_pybind.hpp"
 #include "ttnn/operations/experimental/transformer/all_reduce_create_qkv_heads/all_reduce_create_qkv_heads_pybind.hpp"
 #include "ttnn/operations/experimental/unary_backward/gelu_backward/gelu_backward_pybind.hpp"
 #include "ttnn/operations/experimental/reduction/sort/sort_pybind.hpp"
 #include "ttnn/operations/experimental/gather/gather_pybind.hpp"
 #include "ttnn/operations/experimental/gather/tosa/gather_tosa_pybind.hpp"
+#include "ttnn/operations/experimental/padded_slice/padded_slice_pybind.hpp"
 
 namespace ttnn::operations::experimental {
 
 void py_module(py::module& module) {
     slice_write::bind_slice_write(module);
+    padded_slice::bind_padded_slice(module);
 
     transformer::detail::bind_concatenate_heads(module);
     transformer::detail::bind_split_qkv(module);
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/padded_slice_reader_unary_unpad_dims_rm_interleaved_start_id.cpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/padded_slice_reader_unary_unpad_dims_rm_interleaved_start_id.cpp
new file mode 100644
index 000000000000..9288c1108d71
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/padded_slice_reader_unary_unpad_dims_rm_interleaved_start_id.cpp
@@ -0,0 +1,79 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include "dataflow_api.h"
+#include "debug/dprint.h"
+#include "ttnn/cpp/ttnn/operations/data_movement/common/kernels/common.hpp"
+
+void kernel_main() {
+    const uint32_t src_addr = get_arg_val<uint32_t>(0);
+    const uint32_t padded_stick_size = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_stick_size = get_arg_val<uint32_t>(2);
+    const uint32_t stick_size_offset = get_arg_val<uint32_t>(3);
+    const uint32_t num_dims = get_arg_val<uint32_t>(4);
+    const uint32_t start_id = get_arg_val<uint32_t>(5);
+    const uint32_t num_sticks_per_core = get_arg_val<uint32_t>(6);
+    const uint32_t num_sticks_per_core_read = get_arg_val<uint32_t>(7);
+    const uint32_t num_read_per_barrier = get_arg_val<uint32_t>(8);
+    const uint32_t num_unpadded_sticks_addr = get_arg_addr(9);
+
+    tt_l1_ptr uint32_t* num_unpadded_sticks = (tt_l1_ptr uint32_t*)(num_unpadded_sticks_addr);
+    volatile tt_l1_ptr uint32_t* num_padded_sticks = num_unpadded_sticks + num_dims;
+    volatile tt_l1_ptr uint32_t* id_per_dim = num_padded_sticks + num_dims;
+
+    constexpr bool src0_is_dram = get_compile_time_arg_val(0) == 1;
+    constexpr uint32_t misalignment = get_compile_time_arg_val(1);
+    uint32_t read_size = unpadded_stick_size + misalignment;
+
+    const InterleavedAddrGen<src0_is_dram> s0 = {.bank_base_address = src_addr, .page_size = padded_stick_size};
+
+    constexpr uint32_t cb_id_in0 = 0;
+
+    uint32_t src_stick_id = start_id;
+    uint32_t sticks_read = 0;
+#ifdef DEBUG
+    DPRINT << "src_addr: " << src_addr << ", padded_stick_size: " << padded_stick_size
+           << ", unpadded_stick_size: " << unpadded_stick_size << ", stick_size_offset: " << stick_size_offset
+           << ", num_dims: " << num_dims << ", start_id: " << start_id
+           << ", num_sticks_per_core: " << num_sticks_per_core
+           << ", num_sticks_per_core_read: " << num_sticks_per_core_read
+           << ", num_read_per_barrier: " << num_read_per_barrier << ENDL();
+
+    DPRINT << "misalignment: " << misalignment << ", read_size: " << read_size << ", src_stick_id: " << src_stick_id
+           << ", sticks_read: " << sticks_read << ENDL();
+
+    DPRINT << "num_unpadded_sticks: " << num_unpadded_sticks[0] << " " << num_unpadded_sticks[1] << " "
+           << num_unpadded_sticks[2] << " " << num_unpadded_sticks[3] << " " << ENDL();
+    DPRINT << "num_padded_sticks: " << num_padded_sticks[0] << " " << num_padded_sticks[1] << " "
+           << num_padded_sticks[2] << " " << num_padded_sticks[3] << " " << ENDL();
+    DPRINT << "Out CB Page size: " << get_local_cb_interface(cb_id_in0).fifo_page_size << ENDL();
+#endif
+    const uint32_t base_src_buffer_l1_addr = get_write_ptr(cb_id_in0);
+    const uint64_t base_noc_addr = get_noc_addr(0, s0);
+
+    for (uint32_t iter = 0; iter < num_sticks_per_core_read and sticks_read < num_sticks_per_core; ++iter) {
+        cb_reserve_back(cb_id_in0, num_read_per_barrier);
+        uint32_t src_buffer_l1_addr = get_write_ptr(cb_id_in0);
+
+        for (uint32_t i = 0; i < num_read_per_barrier and sticks_read < num_sticks_per_core; ++i) {
+            sticks_read++;
+            uint64_t src_noc_addr = get_noc_addr(src_stick_id, s0);
+            noc_async_read(src_noc_addr, src_buffer_l1_addr, unpadded_stick_size);
+            src_buffer_l1_addr += stick_size_offset;
+            src_stick_id++;
+            for (uint32_t j = 0; j < num_dims; j++) {
+                id_per_dim[j]++;
+                if (id_per_dim[j] == num_unpadded_sticks[j]) {
+                    id_per_dim[j] = 0;
+                    src_stick_id += num_padded_sticks[j];
+                } else {
+                    break;
+                }
+            }
+        }
+        noc_async_read_barrier();
+        cb_push_back(cb_id_in0, num_read_per_barrier);
+    }
+}
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
new file mode 100644
index 000000000000..cbdacbb2dad7
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
+    }
+    uint32_t write_addr = out_addr + unpadded_row_size_bytes;
+    uint64_t pad_noc_addr = get_noc_addr(pad_addr + unpadded_row_size_bytes);
+    for (uint32_t i = 0; i < num_units; ++i) {
+        noc_async_read(pad_noc_addr, write_addr, pad_size_bytes);
+        write_addr += padded_row_size_bytes;
+    }
+    cb_wait_front(cb_id_out, num_units);
+}
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
new file mode 100644
index 000000000000..dd3cebd3329e
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
@@ -0,0 +1,93 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(input_tensor_a.get_padded_shape().rank() == 4, "Only 4D tensors are supported for padded_slice");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Padded slice start, end and input tensor must all have the same rank");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(
+            this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i],
+            "Starts {} must be less than the shape of the tensor {} at index {}",
+            this->padded_slice_start[i],
+            input_tensor_a.get_padded_shape()[i],
+            i);
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(
+            this->padded_slice_start[i] <= this->padded_slice_end[i],
+            "Slice start {} must be less than or equal to the end {}",
+            this->padded_slice_start[i],
+            this->padded_slice_end[i]);
+    }
+    if (!output_tensors.empty() && output_tensors[0].has_value()) {
+        const auto output_shape_required = compute_output_specs(input_tensors)[0].logical_shape();
+        const auto& out_tensor = output_tensors[0].value();
+        TT_FATAL(
+            out_tensor.get_padded_shape() == output_shape_required,
+            "The input tensors need a shape of {}, however the output tensor is only {}",
+            output_shape_required,
+            out_tensor.get_padded_shape());
+    }
+    auto output_tensor_shape = this->compute_output_specs(input_tensors)[0].logical_shape();
+    TT_FATAL(!has_step, "Padded slice does not support strided slices");
+}
+
+std::vector<ttnn::TensorSpec> PaddedSliceDeviceOperation::compute_output_specs(
+    const std::vector<Tensor>& input_tensors) const {
+    const auto& input_tensor = input_tensors[0];
+    SmallVector<uint32_t> out_shape(input_tensor.get_logical_shape().rank());
+
+    TT_FATAL(out_shape.size() == 4, "Only 4D tensors are supported for padded_slice");
+    auto output_dim_i = [this](size_t i) {
+        return (this->padded_slice_end[i] - this->padded_slice_start[i] + this->step[i] - 1) / this->step[i];
+    };
+    for (uint32_t i = 0; i < out_shape.size(); i++) {
+        out_shape[i] = output_dim_i(i);
+    }
+    out_shape[2] = out_shape[0] * out_shape[1] * out_shape[2];
+    out_shape[0] = 1;
+    out_shape[1] = 1;
+
+    if (this->output_mem_config.memory_layout() == TensorMemoryLayout::HEIGHT_SHARDED) {
+        auto output_shard_shape = this->output_mem_config.shard_spec().value().shape;
+        out_shape[out_shape.size() - 1] = output_shard_shape[1];
+    }
+
+    ttnn::Shape output_tensor_shape(std::move(out_shape));
+    auto tensor_layout = TensorLayout(input_tensor.get_dtype(), PageConfig(Layout::ROW_MAJOR), this->output_mem_config);
+    return {ttnn::TensorSpec(output_tensor_shape, tensor_layout)};
+}
+
+operation::ProgramWithCallbacks PaddedSliceDeviceOperation::create_program(
+    const std::vector<Tensor>& input_tensors, std::vector<Tensor>& output_tensors) const {
+    const auto& input_tensor_a = input_tensors.at(0);
+    auto& output_tensor = output_tensors.at(0);
+
+    return detail::padded_slice_multi_core(
+        input_tensor_a, output_tensor, this->padded_slice_start, this->padded_slice_end, this->step);
+}
+
+}  // namespace ttnn::operations::experimental
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.hpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.hpp
new file mode 100644
index 000000000000..ef63f998373b
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.hpp
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <optional>
+
+#include "ttnn/tensor/tensor.hpp"
+#include "ttnn/run_operation.hpp"
+
+namespace ttnn::operations::experimental {
+
+struct PaddedSliceDeviceOperation {
+    const ttnn::Shape padded_slice_start;
+    const ttnn::Shape padded_slice_end;
+    const ttnn::Shape step;
+    const tt::tt_metal::MemoryConfig output_mem_config;
+
+    void validate_with_output_tensors(
+        const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const;
+    std::vector<ttnn::TensorSpec> compute_output_specs(const std::vector<Tensor>& input_tensors) const;
+    tt::tt_metal::operation::ProgramWithCallbacks create_program(
+        const std::vector<Tensor>& input_tensors, std::vector<Tensor>& output_tensors) const;
+};
+
+}  // namespace ttnn::operations::experimental
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
new file mode 100644
index 000000000000..043704cc69ac
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
@@ -0,0 +1,407 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    auto num_cores_total = cores.size();
+
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+    bool is_block_sharded = output_tensor.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+    uint32_t num_cores_channels = 1;
+    auto total_cores = output_shard_spec.grid;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+    uint32_t input_page_size = input_shape[-1] * input_tensor.element_size();
+    uint32_t input_row_size_bytes = input_shape[-1] * input_tensor.element_size() / num_cores_channels;
+
+    uint32_t output_row_size_bytes = output_shard_shape[1] * input_tensor.element_size();
+    uint32_t output_row_size_elems = output_shard_shape[1];
+
+    tt::log_debug("input_row_size_bytes: {}, output_row_size_bytes: {}", input_row_size_bytes, output_row_size_bytes);
+    std::uint32_t num_dims = static_cast<std::uint32_t>(input_shape.rank());
+    std::vector<uint32_t> num_output_sticks_per_dim(num_dims);
+    std::vector<uint32_t> num_input_sticks_per_dim(num_dims);
+    std::vector<uint32_t> id_per_dim(num_dims);
+
+    std::vector<uint32_t> accumulated_total_per_dim(num_dims);
+
+    // TODO: Remove first element of these arrays and update kernel accordingly
+    // This currently just matches tile version where we iterate over the row as well
+    num_output_sticks_per_dim[0] = 1;
+    num_input_sticks_per_dim[0] = 0;
+    accumulated_total_per_dim[0] = 1;
+
+    tt::log_debug("Output Shape : {}, Input Shape : {}", actual_output_shape, input_shape);
+    for (int32_t i = 1; i < num_dims; i++) {
+        uint32_t num_output_dim = actual_output_shape[-(i + 1)];
+        uint32_t num_total_dim = input_shape[-(i + 1)];
+        uint32_t num_input_dim = (num_total_dim - num_output_dim) * accumulated_total_per_dim[i - 1];
+        num_output_sticks_per_dim[i] = num_output_dim;
+        num_input_sticks_per_dim[i] = num_input_dim;
+        accumulated_total_per_dim[i] = num_total_dim * accumulated_total_per_dim[i - 1];
+    }
+
+    for (int i = 0; i < num_dims; i++) {
+        tt::log_debug(
+            "i = {}, num_output_sticks_per_dim: {}, num_input_sticks_per_dim: {}, accumulated_total_per_dim: {}",
+            i,
+            num_output_sticks_per_dim[i],
+            num_input_sticks_per_dim[i],
+            accumulated_total_per_dim[i]);
+    }
+    using namespace tt::tt_metal::experimental;
+    auto src_buffer_alignment = input_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto dst_buffer_alignment = output_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto alignment = std::max(src_buffer_alignment, dst_buffer_alignment);
+    uint32_t begins_bytes = output_tensor_start[-1] * input_tensor.element_size();
+    uint32_t misalignment = begins_bytes % src_buffer_alignment;
+
+    uint32_t output_row_size_bytes_offset = tt::round_up(output_row_size_bytes, alignment);
+    uint32_t start_addr = input_tensor.buffer()->address();
+    std::vector<uint32_t> common_reader_kernel_args = {
+        start_addr + begins_bytes - misalignment,  // read from nearest aligned address
+        input_page_size,
+        input_row_size_bytes,
+        output_row_size_bytes_offset,
+        num_dims,
+        0,
+        0,
+        0,
+        0};
+
+    common_reader_kernel_args.insert(
+        common_reader_kernel_args.end(), num_output_sticks_per_dim.begin(), num_output_sticks_per_dim.end());
+    common_reader_kernel_args.insert(
+        common_reader_kernel_args.end(), num_input_sticks_per_dim.begin(), num_input_sticks_per_dim.end());
+
+    std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> ret_val(num_cores_total);
+
+    const auto num_sticks_per_core = output_shard_spec.shape[0];
+    uint32_t num_sticks_per_core_read = 0, num_read_per_barrier = 0;
+    if (num_sticks_per_core != 0) {
+        num_sticks_per_core_read =
+            tt::tt_metal::merge_num_sticks_to_read(num_sticks_per_core, output_row_size_bytes_offset, max_read_size);
+        num_read_per_barrier = num_sticks_per_core / num_sticks_per_core_read;
+    }
+    tt::log_debug(
+        "num_stick_per_core: {}, num_stick_per_core_read: {}, num_read_per_barrier: {}",
+        num_sticks_per_core,
+        num_sticks_per_core_read,
+        num_read_per_barrier);
+
+    uint32_t start_offset = get_rm_start_offset(input_tensor, output_tensor_start);
+    uint32_t core_index = 0;
+    for (const auto& core : cores) {
+        uint32_t core_w_index = 0;
+        uint32_t core_h_index = core_index;
+        if (is_block_sharded) {
+            core_w_index = rm_orientation ? core.x : core.y;
+            core_h_index = rm_orientation ? core.y : core.x;
+        }
+
+        const uint32_t num_sticks_written = core_h_index * num_sticks_per_core;
+        const uint32_t width_offset = core_w_index * output_row_size_bytes_offset;
+
+        id_per_dim[0] = num_sticks_written % num_output_sticks_per_dim[0];
+        uint32_t output_written = num_sticks_written / num_output_sticks_per_dim[0];
+        uint32_t start_id = id_per_dim[0] + start_offset;
+        for (uint32_t j = 1; j < num_dims; j++) {
+            id_per_dim[j] = output_written % num_output_sticks_per_dim[j];
+            output_written = output_written / num_output_sticks_per_dim[j];
+            start_id += id_per_dim[j] * accumulated_total_per_dim[j - 1];
+        }
+
+        std::vector<uint32_t> reader_kernel_args = common_reader_kernel_args;
+        reader_kernel_args[0] += width_offset;
+
+        uint32_t addr_offset = 5;
+        reader_kernel_args[addr_offset++] = start_id;
+        reader_kernel_args[addr_offset++] = num_sticks_per_core;
+        reader_kernel_args[addr_offset++] = num_sticks_per_core;
+        reader_kernel_args[addr_offset] = num_sticks_per_core;
+        reader_kernel_args.insert(reader_kernel_args.end(), id_per_dim.begin(), id_per_dim.end());
+
+        std::vector<uint32_t> writer_kernel_args = {
+            num_sticks_per_core, output_row_size_elems, input_row_size_bytes, output_row_size_bytes};
+        ret_val[core_index] = {reader_kernel_args, writer_kernel_args};
+        core_index++;
+    }
+
+    return ret_val;
+}
+
+operation::ProgramWithCallbacks padded_slice_rm_multi_core(
+    const Tensor& a, Tensor& output, const ttnn::Shape& output_tensor_start, const ttnn::Shape& output_tensor_end) {
+    const ttnn::Shape output_shape = output.get_logical_shape();
+    ttnn::Shape actual_output_shape = output_tensor_end;
+    for (int i = 0; i < output_shape.rank(); i++) {
+        actual_output_shape[i] = output_tensor_end[i] - output_tensor_start[i];
+    }
+
+    tt::tt_metal::Program program = tt::tt_metal::CreateProgram();
+
+    // This should allocate a DRAM buffer on the device
+    tt::tt_metal::IDevice* device = a.device();
+
+    uint32_t num_unpadded_sticks = output.volume() / output.get_padded_shape()[-1];
+
+    auto compute_with_storage_grid_size = device->compute_with_storage_grid_size();
+    uint32_t num_cores_x = compute_with_storage_grid_size.x;
+    uint32_t num_cores_y = compute_with_storage_grid_size.y;
+
+    tt::tt_metal::Buffer* src0_buffer = a.buffer();
+
+    tt::DataFormat cb_data_format = tt::tt_metal::datatype_to_dataformat_converter(a.get_dtype());
+
+    bool is_output_sharded = output.is_sharded();
+
+    TT_FATAL(is_output_sharded, "Output Tensor must be sharded.");
+    auto output_shard_spec = output.shard_spec().value();
+
+    bool is_block_sharded = output.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+
+    uint32_t output_row_size_bytes = output_shard_spec.shape[1] * output.element_size();
+
+    CoreRangeSet total_cores = output.shard_spec().value().grid;
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+
+    std::vector<CoreCoord> iter_cores = corerange_to_cores(total_cores, std::nullopt, rm_orientation);
+    uint32_t num_cores_total = total_cores.num_cores();
+
+    uint32_t num_cores_channels = 1;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+
+    bool pad_output_row = false;
+
+    TT_FATAL(
+        a.get_logical_shape()[3] % num_cores_channels == 0,
+        "Input tensor should be divisible by number of cores in channel dimension");
+    uint32_t input_row_size_bytes = a.get_logical_shape()[-1] * a.element_size();
+    input_row_size_bytes = input_row_size_bytes / num_cores_channels;
+
+    tt::tt_metal::Buffer* dst_buffer = output.buffer();
+    TT_ASSERT(dst_buffer != nullptr, "Output buffer should be allocated on device!");
+
+    bool src0_is_dram = src0_buffer->buffer_type() == tt::tt_metal::BufferType::DRAM;
+    bool dst_is_dram = dst_buffer->buffer_type() == tt::tt_metal::BufferType::DRAM;
+
+    uint32_t src_stick_size = input_row_size_bytes;
+    uint32_t dst_stick_size = output_row_size_bytes;
+
+    uint32_t src0_cb_index = 0;
+    uint32_t temp_pad_cb_index = 1;
+    uint32_t max_read_size = 4096;
+
+    auto src_buffer_alignment = a.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? ::hal::get_dram_alignment()
+                                    : ::hal::get_l1_alignment();
+    auto dst_buffer_alignment = output.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? ::hal::get_dram_alignment()
+                                    : ::hal::get_l1_alignment();
+    auto alignment = std::max(src_buffer_alignment, dst_buffer_alignment);
+
+    // if begins is not aligned then we need to pad the cb size, so that we can read from the nearest aligned address
+    uint32_t begins_bytes = output_tensor_start[-1] * a.element_size();
+    uint32_t misalignment = begins_bytes % src_buffer_alignment;
+
+    if (misalignment != 0) {
+        alignment *= 2;
+    }
+    uint32_t cb_page_size = tt::round_up(output_row_size_bytes, alignment);
+
+    CBHandle cb_src0, cb_temp_padded_row;
+    uint32_t num_output_sticks_per_core = output_shard_spec.shape[0];
+    tt::tt_metal::CircularBufferConfig cb_src0_config =
+        tt::tt_metal::CircularBufferConfig(
+            num_output_sticks_per_core * output_row_size_bytes, {{src0_cb_index, cb_data_format}})
+            .set_page_size(src0_cb_index, output_row_size_bytes)
+            .set_globally_allocated_address(*output.buffer());
+    cb_src0 = tt::tt_metal::CreateCircularBuffer(program, total_cores, cb_src0_config);
+    if (output_row_size_bytes > input_row_size_bytes) {
+        pad_output_row = true;
+        tt::tt_metal::CircularBufferConfig cb_temp_pad_config =
+            tt::tt_metal::CircularBufferConfig(1 * output_row_size_bytes, {{temp_pad_cb_index, cb_data_format}})
+                .set_page_size(temp_pad_cb_index, output_row_size_bytes);
+    }
+
+    std::vector<uint32_t> writer_compile_time_args_vec = {(std::uint32_t)src0_cb_index, (std::uint32_t)dst_is_dram};
+
+    std::vector<uint32_t> reader_compile_time_args_vec = {(std::uint32_t)src0_is_dram, misalignment};
+    tt::tt_metal::KernelHandle unary_reader_kernel_id = tt::tt_metal::CreateKernel(
+        program,
+        "ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/"
+        "padded_slice_reader_unary_unpad_dims_rm_interleaved_start_id.cpp",
+        total_cores,
+        tt::tt_metal::ReaderDataMovementConfig(reader_compile_time_args_vec));
+
+    tt::tt_metal::KernelHandle unary_writer_kernel_id;
+    if (pad_output_row) {
+        writer_compile_time_args_vec.push_back((std::uint32_t)temp_pad_cb_index);
+        unary_writer_kernel_id = tt::tt_metal::CreateKernel(
+            program,
+            "ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/"
+            "writer_unary_sharded_padded.cpp",
+            total_cores,
+            tt::tt_metal::WriterDataMovementConfig(writer_compile_time_args_vec));
+    } else {
+        unary_writer_kernel_id = tt::tt_metal::CreateKernel(
+            program,
+            "ttnn/cpp/ttnn/operations/data_movement/sharded/device/kernels/dataflow/writer_unary_sharded.cpp",
+            total_cores,
+            tt::tt_metal::WriterDataMovementConfig(writer_compile_time_args_vec));
+    }
+
+    auto all_runtime_args = get_padded_slice_runtime_args_rm_sharded_output(
+        a, output, output_tensor_start, actual_output_shape, iter_cores, max_read_size);
+
+    uint32_t i = 0;
+    for (const auto& core : iter_cores) {
+        tt::tt_metal::SetRuntimeArgs(program, unary_reader_kernel_id, core, all_runtime_args[i].first);
+        tt::tt_metal::SetRuntimeArgs(program, unary_writer_kernel_id, core, all_runtime_args[i].second);
+        i++;
+    }
+
+    auto override_runtime_args_callback = [unary_reader_kernel_id,
+                                           unary_writer_kernel_id,
+                                           output_tensor_start,
+                                           actual_output_shape,
+                                           compute_with_storage_grid_size,
+                                           max_read_size,
+                                           iter_cores,
+                                           cb_src0](
+                                              const void* operation,
+                                              Program& program,
+                                              const std::vector<Tensor>& input_tensors,
+                                              const std::vector<std::optional<const Tensor>>&,
+                                              const std::vector<Tensor>& output_tensors) {
+        auto src_tensor = input_tensors.at(0);
+        auto dst_tensor = output_tensors.at(0);
+        TT_FATAL(dst_tensor.is_sharded(), "Output tensor must be sharded");
+        UpdateDynamicCircularBufferAddress(program, cb_src0, *src_tensor.buffer());
+
+        auto all_runtime_args = get_padded_slice_runtime_args_rm_sharded_output(
+            src_tensor, dst_tensor, output_tensor_start, actual_output_shape, iter_cores, max_read_size);
+
+        uint32_t i = 0;
+        for (const auto& core : iter_cores) {
+            tt::tt_metal::SetRuntimeArgs(program, unary_reader_kernel_id, core, all_runtime_args[i].first);
+            tt::tt_metal::SetRuntimeArgs(program, unary_writer_kernel_id, core, all_runtime_args[i].second);
+            i++;
+        }
+
+    };
+
+    return {.program = std::move(program), .override_runtime_arguments_callback = override_runtime_args_callback};
+}
+
+operation::ProgramWithCallbacks padded_slice_multi_core(
+    const Tensor& a,
+    Tensor& output,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& output_tensor_end,
+    const ttnn::Shape& step) {
+    bool has_step = false;
+    for (int i = 0; i < step.size(); i++) {
+        if (step[i] != 1) {
+            has_step = true;
+            break;
+        }
+    }
+    TT_FATAL(!has_step, "Padded Slice with Stride is not supported yet");
+    TT_FATAL(a.get_layout() == Layout::ROW_MAJOR, "Input must be in row major format for padded_slice operation");
+    TT_FATAL(
+        output.is_sharded(),
+        "Output must be sharded for the padded_slice operation. Use slice for non-sharded outputs");
+    TT_FATAL(!a.is_sharded(), " Sharded input is not supported for padded_slice operation");
+
+    return padded_slice_rm_multi_core(a, output, output_tensor_start, output_tensor_end);
+}
+
+}  // namespace ttnn::operations::experimental::detail
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.hpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.hpp
new file mode 100644
index 000000000000..cef52fa7e590
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.hpp
@@ -0,0 +1,17 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#include <tt-metalium/host_api.hpp>
+
+namespace ttnn::operations::experimental::detail {
+
+tt::tt_metal::operation::ProgramWithCallbacks padded_slice_multi_core(
+    const Tensor& a,
+    Tensor& output,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& output_tensor_end,
+    const ttnn::Shape& step);
+
+}  // namespace ttnn::operations::experimental::detail
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.cpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.cpp
new file mode 100644
index 000000000000..35f223ec2318
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.cpp
@@ -0,0 +1,176 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "device/padded_slice_op.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/run_operation.hpp"
+#include "ttnn/common/constants.hpp"
+#include "ttnn/common/queue_id.hpp"
+#include "ttnn/operations/creation.hpp"
+#include "ttnn/operations/core/core.hpp"
+#include "ttnn/operations/data_movement/common/common.hpp"
+#include "ttnn/operations/data_movement/fill_pad/fill_pad.hpp"
+#include "ttnn/operations/experimental/reshape/view.hpp"
+#include "ttnn/tensor/types.hpp"
+#include "padded_slice.hpp"
+
+namespace ttnn::operations::experimental {
+
+template <typename T>
+ttnn::Tensor PaddedSliceOperation::invoke(
+    QueueId queue_id,
+    const ttnn::Tensor& input_tensor,
+    tt::stl::Span<const T> begins,
+    tt::stl::Span<const T> ends,
+    tt::stl::Span<const T> step,
+    const MemoryConfig& memory_config,
+    const std::optional<Tensor>& optional_output_tensor,
+    const std::optional<float>& pad_value) {
+    // Ensure start and end vectors have matching sizes and correct tensor rank
+
+    const auto& input_shape = input_tensor.get_logical_shape();
+    uint32_t input_rank = input_shape.rank();
+    auto input_layout = input_tensor.get_layout();
+
+    TT_FATAL(input_rank == 4, "Only 4D tensors are supported for padded_slice");
+
+    TT_FATAL(
+        input_rank == begins.size(), "Input rank {} and begins {} must have the same size", input_rank, begins.size());
+    TT_FATAL(begins.size() == ends.size(), "Start {} and end {} must have the same size", begins.size(), ends.size());
+    TT_FATAL(
+        step.size() == begins.size(),
+        "Step {} must have the same size as start {} and end",
+        step.size(),
+        begins.size());
+
+    bool no_step = std::ranges::all_of(step, [](uint32_t s) { return s == 1; });
+    bool starts_zero = std::ranges::all_of(begins, [](uint32_t s) { return s == 0; });
+    bool ends_max = true;
+    for (size_t i = 0; i < ends.size(); ++i) {
+        ends_max &= ends[i] == input_shape[i];
+        if (!ends_max) {
+            break;
+        }
+    }
+
+    TT_FATAL(no_step, "Steps != 1 are not supported for padded_slice.");
+    TT_FATAL(input_layout == Layout::ROW_MAJOR, "Only Row Major Inputs are supported for padded_slice.");
+    TT_FATAL(memory_config.is_sharded(), "Output Memory Config must be sharded. Use slice for non-sharded outputs.");
+    TT_FATAL(!input_tensor.memory_config().is_sharded(), " padded_slice does not support sharded inputs.");
+
+    auto ret_adjustment([&](const ttnn::Tensor& ret_input_tensor) {
+        if (ret_input_tensor.storage_type() == StorageType::DEVICE) {
+            auto tensor = ttnn::to_memory_config(ret_input_tensor, memory_config, std::nullopt);
+            tensor = ttnn::to_layout(tensor, input_layout, std::nullopt, std::nullopt, (IDevice*)nullptr);
+            return tensor;
+        }
+        return ret_input_tensor;
+    });
+
+    // No-op check
+    if (no_step && starts_zero && ends_max) {
+        return ret_adjustment(input_tensor);
+    }
+
+    // Create modified vectors with wrapped indices and adjust them to match the tensor's rank
+    ttnn::SmallVector<uint32_t> modified_begins(input_rank, 0);
+    ttnn::SmallVector<uint32_t> modified_ends(input_rank, 0);
+    ttnn::SmallVector<uint32_t> modified_step(input_rank, 1);
+
+    // Wrap indices and adjust begins, ends, and step
+    for (size_t i = 0; i < begins.size(); ++i) {
+        if constexpr (std::is_signed_v<T>) {
+            modified_begins[i] = data_movement::wrap_index(begins[i], input_shape[i]);
+            modified_ends[i] = data_movement::wrap_index(ends[i], input_shape[i]);
+            modified_step[i] = static_cast<uint32_t>(step[i]);
+        } else {
+            modified_begins[i] = begins[i];
+            modified_ends[i] = ends[i];
+            modified_step[i] = step[i];
+        }
+    }
+
+    auto output_dim_i = [&modified_begins, &modified_step](size_t i, const ttnn::SmallVector<uint32_t>& modified_ends) {
+        return (modified_ends[i] - modified_begins[i] + modified_step[i] - 1) / modified_step[i];
+    };
+
+    ttnn::SmallVector<uint32_t> padded_ends = modified_ends;
+
+    ttnn::SmallVector<uint32_t> actual_shape_vec, final_padded_shape_vec;
+    actual_shape_vec.reserve(input_rank);
+    final_padded_shape_vec.reserve(input_rank);
+    bool empty = false;
+
+    // Compute actual and padded shapes for the original input rank
+    for (size_t i = 0; i < input_rank; ++i) {
+        TT_FATAL(
+            modified_ends[i] >= modified_begins[i],
+            "End {} must be greater than or equal to start {}",
+            modified_ends[i],
+            modified_begins[i]);
+        auto val = output_dim_i(i, modified_ends);
+        if (val == 0) {
+            empty = true;
+        }
+        actual_shape_vec.push_back(val);
+        final_padded_shape_vec.push_back(std::max(output_dim_i(i, padded_ends), static_cast<uint32_t>(1)));
+    }
+    ttnn::Shape actual_shape(actual_shape_vec);
+    ttnn::Shape final_padded_shape(final_padded_shape_vec);
+
+    if (empty) {
+        TT_FATAL(
+            input_tensor.storage_type() == StorageType::DEVICE,
+            "Host tensor slice cannot return a scalar or empty tensor");
+        return ttnn::empty(
+            actual_shape, input_tensor.dtype(), input_tensor.layout(), input_tensor.mesh_device(), memory_config);
+    }
+
+    auto res =
+        tt::tt_metal::operation::run(
+            PaddedSliceDeviceOperation{
+                ttnn::Shape(modified_begins), ttnn::Shape(padded_ends), ttnn::Shape(modified_step), memory_config},
+            {input_tensor},
+            {},
+            {optional_output_tensor},
+            queue_id)
+            .at(0);
+
+    // If padded_slice should return a sharded tensor, then the op must created the sharded tensor in the requested
+    // memory config
+    if (res.is_sharded() && memory_config.is_sharded()) {
+        TT_ASSERT(
+            res.memory_config() == memory_config,
+            "Memory config must match. Got {}, expecteed {}",
+            res.memory_config(),
+            memory_config);
+        return res;
+    }
+
+    res = ttnn::experimental::view(res, actual_shape, final_padded_shape);
+
+    return res;
+}
+
+template ttnn::Tensor PaddedSliceOperation::invoke<int>(
+    QueueId queue_id,
+    const ttnn::Tensor& input_tensor,
+    tt::stl::Span<const int> begins,
+    tt::stl::Span<const int> ends,
+    tt::stl::Span<const int> step,
+    const MemoryConfig& memory_config_arg,
+    const std::optional<Tensor>& optional_output_tensor,
+    const std::optional<float>& pad_value);
+
+template ttnn::Tensor PaddedSliceOperation::invoke<uint32_t>(
+    QueueId queue_id,
+    const ttnn::Tensor& input_tensor,
+    tt::stl::Span<const uint32_t> begins,
+    tt::stl::Span<const uint32_t> ends,
+    tt::stl::Span<const uint32_t> step,
+    const MemoryConfig& memory_config_arg,
+    const std::optional<Tensor>& optional_output_tensor,
+    const std::optional<float>& pad_value);
+
+}  // namespace ttnn::operations::experimental
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.hpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.hpp
new file mode 100644
index 000000000000..1fbb125768ef
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.hpp
@@ -0,0 +1,54 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "ttnn/decorators.hpp"
+
+namespace ttnn {
+namespace operations {
+namespace experimental {
+
+struct PaddedSliceOperation {
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        tt::stl::Span<const T> begins,
+        tt::stl::Span<const T> ends,
+        tt::stl::Span<const T> step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const ttnn::SmallVector<T>& begins,
+        const ttnn::SmallVector<T>& ends,
+        const ttnn::SmallVector<T>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt) {
+        return invoke(
+            queue_id,
+            input_tensor,
+            tt::stl::Span<const T>(begins),
+            tt::stl::Span<const T>(ends),
+            tt::stl::Span<const T>(step),
+            memory_config_arg,
+            optional_output_tensor,
+            pad_value);
+    }
+};
+
+}  // namespace experimental
+}  // namespace operations
+}  // namespace ttnn
+
+namespace ttnn::experimental {
+constexpr auto padded_slice =
+    ttnn::register_operation<"ttnn::padded_slice", ttnn::operations::experimental::PaddedSliceOperation>();
+}
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice_pybind.cpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice_pybind.cpp
new file mode 100644
index 000000000000..c9fdf8704016
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice_pybind.cpp
@@ -0,0 +1,79 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "padded_slice_pybind.hpp"
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include "cpp/ttnn-pybind/decorators.hpp"
+
+namespace ttnn::operations::experimental::padded_slice {
+namespace py = pybind11;
+
+void bind_padded_slice(py::module& module) {
+    auto doc =
+        R"doc(
+            Returns a padded_sliced tensor. If the input tensor is on host, the padded_slice will be performed on host, and if its on device it will be performed on device.
+
+            Args:
+                input_tensor: Input Tensor.
+                padded_slice_start: Start indices of input tensor. Values along each dim must be < input_tensor_shape[i].
+                padded_slice_end: End indices of input tensor. Values along each dim must be < input_tensor_shape[i].
+                padded_slice_step: (Optional[List[int[tensor rank]]) Step size for each dim. Default is None, which works out be 1 for each dimension.
+                memory_config: Memory Config of the output tensor. This must be either height or block sharded.
+
+            Keyword Args:
+                queue_id (uint8, optional):command queue id
+
+            Returns:
+                ttnn.Tensor: the output tensor.
+
+            Example:
+                >>> tensor = ttnn.padded_slice(ttnn.from_torch(torch.zeros((1, 1, 64, 32), dtype=torch.bfloat16), device=device), [0, 0, 0, 0], [1, 1, 64, 16], [1, 1, 2, 1])
+                >>> print(tensor.shape)
+                [1, 1, 32, 16]
+                >>> input = ttnn.from_torch(torch.zeros((1, 1, 64, 32), dtype=torch.bfloat16), device=device)
+                >>> output = ttnn.padded_slice(input, [0, 0, 0, 0], [1, 1, 32, 32])
+                >>> print(output.shape)
+                [1, 1, 32, 32]
+                )doc";
+
+    // TODO: implementing the array version and overloading the pybind with all the possible array sizes is better than
+    // a vector with a fixed size default value
+    using OperationType = decltype(ttnn::experimental::padded_slice);
+    ttnn::bind_registered_operation(
+        module,
+        ttnn::experimental::padded_slice,
+        doc,
+        ttnn::pybind_overload_t{
+            [](const OperationType& self,
+               const ttnn::Tensor& input_tensor,
+               const ttnn::SmallVector<int>& padded_slice_start,
+               const ttnn::SmallVector<int>& padded_slice_end,
+               const std::optional<ttnn::SmallVector<int>>& step,
+               const MemoryConfig& memory_config,
+               const std::optional<Tensor>& optional_output_tensor,
+               const std::optional<float>& pad_value,
+               QueueId queue_id) {
+                const auto step_value = step.value_or(ttnn::SmallVector<int>(padded_slice_end.size(), 1));
+                return self(
+                    queue_id,
+                    input_tensor,
+                    padded_slice_start,
+                    padded_slice_end,
+                    step_value,
+                    memory_config,
+                    optional_output_tensor);
+            },
+            py::arg("input_tensor"),
+            py::arg("padded_slice_start"),
+            py::arg("padded_slice_end"),
+            py::arg("padded_slice_step") = std::nullopt,  // should consider a better default value
+            py::kw_only(),
+            py::arg("memory_config"),
+            py::arg("output_tensor") = std::nullopt,
+            py::arg("pad_value") = std::nullopt,
+            py::arg("queue_id") = DefaultQueueId,
+        });
+}
+}  // namespace ttnn::operations::experimental::padded_slice
diff --git a/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice_pybind.hpp b/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice_pybind.hpp
new file mode 100644
index 000000000000..4d0542bcfbd9
--- /dev/null
+++ b/ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice_pybind.hpp
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: © 2025 Tenstorrent AI ULC
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "ttnn-pybind/pybind_fwd.hpp"
+
+#include "padded_slice.hpp"
+#include "ttnn/tensor/types.hpp"
+
+namespace ttnn::operations::experimental::padded_slice {
+namespace py = pybind11;
+
+void bind_padded_slice(py::module& module);
+}  // namespace ttnn::operations::experimental::padded_slice
diff --git a/ttnn/ttnn/__init__.py b/ttnn/ttnn/__init__.py
index 2c5eefc7ae22..94dcfd3e1bb9 100644
--- a/ttnn/ttnn/__init__.py
+++ b/ttnn/ttnn/__init__.py
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+# SPDX-FileCopyrightText: © 2023 Tenstorrent AI ULC
 
 # SPDX-License-Identifier: Apache-2.0
 
@@ -357,6 +357,7 @@ def auto_register_ttnn_cpp_operations(module):
     prepare_conv_bias,
     prepare_conv_transpose2d_weights,
     prepare_conv_transpose2d_bias,
+    SlidingWindowParallelConfig,
 )
 from ttnn._ttnn.operations.conv import (
     convert_conv_weight_tensor_to_tiled_layout,
diff --git a/ttnn/ttnn/operations/conv2d.py b/ttnn/ttnn/operations/conv2d.py
index 7c9428f7b7c2..6aaa46e0342c 100644
--- a/ttnn/ttnn/operations/conv2d.py
+++ b/ttnn/ttnn/operations/conv2d.py
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+# SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
 
 # SPDX-License-Identifier: Apache-2.0
 
@@ -9,6 +9,7 @@
 import math
 import ttnn
 
+SlidingWindowParallelConfig = ttnn._ttnn.operations.sliding_window.ParallelConfig
 Conv2dConfig = ttnn._ttnn.operations.conv.Conv2dConfig
 Conv2dSliceConfig = ttnn._ttnn.operations.conv.Conv2dSliceConfig
 Conv2dSliceHeight = ttnn._ttnn.operations.conv.Conv2dSliceConfig.SliceTypeEnum.SliceHeight


Reviews:
user: login: afuller-TT
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:35:15Z
user: login: sjameelTT
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:39:25Z
user: login: amorrisonTT
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:45:11Z
user: login: amorrisonTT
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:45:21Z
user: login: amorrisonTT
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:45:39Z
user: login: amorrisonTT
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T16:46:26Z
user: login: bbradelTT
body: @ntarafdar how does this fit into TM work?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T21:22:28Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T06:27:49Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T06:27:53Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T06:27:59Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T06:36:51Z
user: login: bbradelTT
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T14:56:41Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T19:32:19Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T07:14:58Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-18T07:19:00Z
user: login: pavlejosipovic
body: I'm approving conv2d bits, but given how tied this implementation of padded_slice  is to conv2d that we even can't do UT w/o importing conv2d functions, maybe it's better to move this op out to ttnn/cpp/ttnn/operations/experimental/cnn/
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T08:28:33Z
user: login: s-jovic
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T10:31:39Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T05:29:10Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T05:29:24Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T05:29:37Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T05:39:11Z
user: login: bbradelTT
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T20:25:49Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T06:47:36Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T06:58:29Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T06:58:58Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:01:18Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:01:56Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:02:07Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:02:54Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:03:39Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:04:14Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:07:12Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:07:48Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:09:26Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:09:32Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:09:49Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:10:00Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:12:16Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:13:27Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:16:38Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:27:04Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:59:18Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T04:57:06Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T04:59:26Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:00:53Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:02:22Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:07:19Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:07:47Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:08:47Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:09:51Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:11:00Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:13:09Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:13:21Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:13:31Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:13:54Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:17:06Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:17:24Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:18:30Z
user: login: sminakov-tt
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:18:52Z
user: login: sminakov-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:25:02Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T08:38:16Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T08:38:25Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T08:55:29Z
user: login: sankarmanoj-tt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T09:00:55Z

Review comments:
diff_hunk: @@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
+    }
+    uint32_t write_addr = out_addr + unpadded_row_size_bytes;
+    uint64_t pad_noc_addr = get_noc_addr(pad_addr + unpadded_row_size_bytes);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: amorrisonTT
body: Consider using `ShardedAddrGen`
created_at: 2025-05-15T16:45:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
+    }
+    uint32_t write_addr = out_addr + unpadded_row_size_bytes;
+    uint64_t pad_noc_addr = get_noc_addr(pad_addr + unpadded_row_size_bytes);
+    for (uint32_t i = 0; i < num_units; ++i) {
+        noc_async_read(pad_noc_addr, write_addr, pad_size_bytes);
+        write_addr += padded_row_size_bytes;
+    }
+    // cb_reserve_back(cb_id_out, num_units);
+    // cb_push_back(cb_id_out, num_units);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: amorrisonTT
body: Commented code
created_at: 2025-05-15T16:45:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1067,3 +1076,108 @@ def test_ttnn_slice_whisper(
 
         ttnn_output = ttnn.to_torch(ttnn_output)
         assert_with_pcc(torch_output, ttnn_output, 0.999)
+
+
+def num_to_core_range_set(x):
+    assert x < 8 or x % 8 == 0
+    num_x = min(x, 8)
+    num_y = x // num_x
+    assert num_x * num_y == x
+    return ttnn.CoreRangeSet(
+        {
+            ttnn.CoreRange(
+                ttnn.CoreCoord(0, 0),
+                ttnn.CoreCoord(num_x - 1, num_y - 1),
+            ),
+        }
+    )
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, cores",
+    [
+        [[2, 256, 256, 56], 128, 16],
+        [[2, 256, 128, 23], 16, 8],
+        [[2, 256, 256, 37], 64, 64],
+        [[2, 8, 8, 32], 2, 4],
+        [[2, 8, 16, 2], 2, 8],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_height_sharded_for_conv2d(device, dims, slice_dim, slice_size, cores, layout, orientation):
+    strides = [1, 1, 1, 1]
+    torch.manual_seed(2005)
+    # torch_input = torch.tensor(range(dims[1] * dims[2])).reshape(1, dims[1], dims[2], 1)
+    # torch_input = torch.broadcast_to(torch_input, dims)
path: tests/ttnn/unit_tests/operations/test_slice.py
user: login: amorrisonTT
body: Commented code
created_at: 2025-05-15T16:45:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,78 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include "dataflow_api.h"
+#include "debug/dprint.h"
+#include "ttnn/cpp/ttnn/operations/data_movement/common/kernels/common.hpp"
+
+void kernel_main() {
+    const uint32_t src_addr = get_arg_val<uint32_t>(0);
+    const uint32_t padded_stick_size = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_stick_size = get_arg_val<uint32_t>(2);
+    const uint32_t stick_size_offset = get_arg_val<uint32_t>(3);
+    const uint32_t num_dims = get_arg_val<uint32_t>(4);
+    const uint32_t start_id = get_arg_val<uint32_t>(5);
+    const uint32_t num_sticks_per_core = get_arg_val<uint32_t>(6);
+    const uint32_t num_sticks_per_core_read = get_arg_val<uint32_t>(7);
+    const uint32_t num_read_per_barrier = get_arg_val<uint32_t>(8);
+
+    tt_l1_ptr uint32_t* num_unpadded_sticks = (tt_l1_ptr uint32_t*)(get_arg_addr(9));
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/padded_slice_reader_unary_unpad_dims_rm_interleaved_start_id.cpp
user: login: amorrisonTT
body: maybe bind `get_arg_addr(9)` to a variable for readability  
created_at: 2025-05-15T16:46:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,6 +10,15 @@
 from models.utility_functions import is_grayskull
 from tests.ttnn.utils_for_testing import assert_with_pcc
 
+import math
+
+
+def round_up(a, b):
path: tests/ttnn/unit_tests/operations/test_slice.py
user: login: bbradelTT
body: This is already defined. Please use the existing one, and possibly move it to a more convenient location.

```
grep 'def round_up' -r * | grep '.py'
models/demos/wormhole/stable_diffusion/tt/ttnn_functional_utility_functions.py:def round_up_to_tile_dim(n):
tests/tt_eager/python_api_testing/unit_testing/misc/test_matmul_1d_gather_in0.py:def round_up(a, b):
```
created_at: 2025-05-15T21:19:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,175 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_tiled_start_offset(const Tensor& input_tensor, const ttnn::Shape& padded_slice_start) {
+    using namespace tt::constants;
+    uint32_t num_input_pages = input_tensor.volume() / (TILE_HW);
+    const auto& shape = input_tensor.get_padded_shape();
+    uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+    uint32_t num_pages_width = num_input_pages / (upper_dims_compressed * (shape[-2] / TILE_HEIGHT));
+
+    // offset for every dim except last 2
+    uint32_t start_offset = get_upper_start_offset(input_tensor, padded_slice_start);
+
+    start_offset += padded_slice_start[-2] / TILE_HEIGHT * num_pages_width + padded_slice_start[-1] / TILE_WIDTH;
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::TILE || input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Error");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i], "Error");
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(this->padded_slice_start[i] <= this->padded_slice_end[i], "Error");
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: bbradelTT
body: Please provide reasonable error messages.
created_at: 2025-05-15T21:21:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1067,3 +1076,108 @@ def test_ttnn_slice_whisper(
 
         ttnn_output = ttnn.to_torch(ttnn_output)
         assert_with_pcc(torch_output, ttnn_output, 0.999)
+
+
+def num_to_core_range_set(x):
+    assert x < 8 or x % 8 == 0
+    num_x = min(x, 8)
+    num_y = x // num_x
+    assert num_x * num_y == x
+    return ttnn.CoreRangeSet(
+        {
+            ttnn.CoreRange(
+                ttnn.CoreCoord(0, 0),
+                ttnn.CoreCoord(num_x - 1, num_y - 1),
+            ),
+        }
+    )
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, cores",
+    [
+        [[2, 256, 256, 56], 128, 16],
+        [[2, 256, 128, 23], 16, 8],
+        [[2, 256, 256, 37], 64, 64],
+        [[2, 8, 8, 32], 2, 4],
+        [[2, 8, 16, 2], 2, 8],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_height_sharded_for_conv2d(device, dims, slice_dim, slice_size, cores, layout, orientation):
+    strides = [1, 1, 1, 1]
+    torch.manual_seed(2005)
+    # torch_input = torch.tensor(range(dims[1] * dims[2])).reshape(1, dims[1], dims[2], 1)
+    # torch_input = torch.broadcast_to(torch_input, dims)
path: tests/ttnn/unit_tests/operations/test_slice.py
user: login: sankarmanoj-tt
body: Removed
created_at: 2025-05-16T06:27:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
+    }
+    uint32_t write_addr = out_addr + unpadded_row_size_bytes;
+    uint64_t pad_noc_addr = get_noc_addr(pad_addr + unpadded_row_size_bytes);
+    for (uint32_t i = 0; i < num_units; ++i) {
+        noc_async_read(pad_noc_addr, write_addr, pad_size_bytes);
+        write_addr += padded_row_size_bytes;
+    }
+    // cb_reserve_back(cb_id_out, num_units);
+    // cb_push_back(cb_id_out, num_units);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: sankarmanoj-tt
body: Removed
created_at: 2025-05-16T06:27:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,78 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include "dataflow_api.h"
+#include "debug/dprint.h"
+#include "ttnn/cpp/ttnn/operations/data_movement/common/kernels/common.hpp"
+
+void kernel_main() {
+    const uint32_t src_addr = get_arg_val<uint32_t>(0);
+    const uint32_t padded_stick_size = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_stick_size = get_arg_val<uint32_t>(2);
+    const uint32_t stick_size_offset = get_arg_val<uint32_t>(3);
+    const uint32_t num_dims = get_arg_val<uint32_t>(4);
+    const uint32_t start_id = get_arg_val<uint32_t>(5);
+    const uint32_t num_sticks_per_core = get_arg_val<uint32_t>(6);
+    const uint32_t num_sticks_per_core_read = get_arg_val<uint32_t>(7);
+    const uint32_t num_read_per_barrier = get_arg_val<uint32_t>(8);
+
+    tt_l1_ptr uint32_t* num_unpadded_sticks = (tt_l1_ptr uint32_t*)(get_arg_addr(9));
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/padded_slice_reader_unary_unpad_dims_rm_interleaved_start_id.cpp
user: login: sankarmanoj-tt
body: Done
created_at: 2025-05-16T06:27:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,175 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_tiled_start_offset(const Tensor& input_tensor, const ttnn::Shape& padded_slice_start) {
+    using namespace tt::constants;
+    uint32_t num_input_pages = input_tensor.volume() / (TILE_HW);
+    const auto& shape = input_tensor.get_padded_shape();
+    uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+    uint32_t num_pages_width = num_input_pages / (upper_dims_compressed * (shape[-2] / TILE_HEIGHT));
+
+    // offset for every dim except last 2
+    uint32_t start_offset = get_upper_start_offset(input_tensor, padded_slice_start);
+
+    start_offset += padded_slice_start[-2] / TILE_HEIGHT * num_pages_width + padded_slice_start[-1] / TILE_WIDTH;
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::TILE || input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Error");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i], "Error");
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(this->padded_slice_start[i] <= this->padded_slice_end[i], "Error");
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: Fixed. 
created_at: 2025-05-16T06:36:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: bbradelTT
body: Please either figure this out or create an issue and reference it here.
created_at: 2025-05-16T14:50:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
+    }
+    uint32_t write_addr = out_addr + unpadded_row_size_bytes;
+    uint64_t pad_noc_addr = get_noc_addr(pad_addr + unpadded_row_size_bytes);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: bbradelTT
body: I second this opinion.
created_at: 2025-05-16T14:51:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
+    }
+    uint32_t write_addr = out_addr + unpadded_row_size_bytes;
+    uint64_t pad_noc_addr = get_noc_addr(pad_addr + unpadded_row_size_bytes);
+    for (uint32_t i = 0; i < num_units; ++i) {
+        noc_async_read(pad_noc_addr, write_addr, pad_size_bytes);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: bbradelTT
body: Are there any potential alignment issues for BH and beyond?

Please test on BH with something that may trigger mis-alignment.
created_at: 2025-05-16T14:56:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
+    }
+    uint32_t write_addr = out_addr + unpadded_row_size_bytes;
+    uint64_t pad_noc_addr = get_noc_addr(pad_addr + unpadded_row_size_bytes);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: sankarmanoj-tt
body: What does ShardedAddrGen do? What would be the advantage of using it?
created_at: 2025-05-16T19:32:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: sankarmanoj-tt
body: I've created an issue. 
created_at: 2025-05-18T07:14:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
+    }
+    uint32_t write_addr = out_addr + unpadded_row_size_bytes;
+    uint64_t pad_noc_addr = get_noc_addr(pad_addr + unpadded_row_size_bytes);
+    for (uint32_t i = 0; i < num_units; ++i) {
+        noc_async_read(pad_noc_addr, write_addr, pad_size_bytes);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: sankarmanoj-tt
body: This operation is intended to be mis-aligned. The mis-aligned is the same for both the source & destination, which works on WH. Does the same hold true for BH?
created_at: 2025-05-18T07:19:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "padded_slice_pybind.hpp"
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include "cpp/ttnn-pybind/decorators.hpp"
+
+namespace ttnn::operations::experimental::padded_slice {
+namespace py = pybind11;
+
+void bind_padded_slice(py::module& module) {
+    auto doc =
+        R"doc(
+            Returns a padded_sliced tensor. If the input tensor is on host, the padded_slice will be performed on host, and if its on device it will be performed on device.
+
+            Args:
+                input_tensor: Input Tensor.
+                padded_slice_start: Start indices of input tensor. Values along each dim must be < input_tensor_shape[i].
+                padded_slice_end: End indices of input tensor. Values along each dim must be < input_tensor_shape[i].
+                padded_slice_step: (Optional[List[int[tensor rank]]) Step size for each dim. Default is None, which works out be 1 for each dimension.
+
+            Keyword Args:
+                memory_config Memory Config of the output tensor
+                queue_id (uint8, optional) command queue id
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice_pybind.cpp
user: login: s-jovic
body: missing semicolons
created_at: 2025-05-19T09:43:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1077,3 +1079,97 @@ def test_ttnn_slice_whisper(
 
         ttnn_output = ttnn.to_torch(ttnn_output)
         assert_with_pcc(torch_output, ttnn_output, 0.999)
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, cores",
+    [
+        [[2, 256, 256, 56], 128, 16],
+        [[2, 256, 128, 23], 16, 8],
+        [[2, 256, 256, 37], 64, 64],
+        [[2, 8, 8, 32], 2, 4],
+        [[2, 8, 16, 2], 2, 8],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_height_sharded_for_conv2d(device, dims, slice_dim, slice_size, cores, layout, orientation):
+    core_grid = device.compute_with_storage_grid_size()
+    if core_grid.x * core_grid.y < cores:
+        pytest.skip("Device does not have enough cores")
+
+    strides = [1, 1, 1, 1]
+    torch.manual_seed(2005)
+    torch_input = torch.randint(-10, 10, dims)
+    core_range = ttnn.num_cores_to_corerangeset(cores, core_grid, orientation == ttnn.ShardOrientation.ROW_MAJOR)
+    num_slices = dims[slice_dim] // slice_size
+    ttnn_input = ttnn.from_torch(
+        torch_input, device=device, layout=layout, dtype=ttnn.bfloat16, memory_config=ttnn.DRAM_MEMORY_CONFIG
+    )
+    parallel_config = ttnn.SlidingWindowParallelConfig(
+        grid=core_range, shard_scheme=ttnn.TensorMemoryLayout.HEIGHT_SHARDED, shard_orientation=orientation
+    )
+    padded_channels = round_up(dims[-1], 32)
+    padded_torch_input = torch.nn.functional.pad(torch_input, (0, padded_channels - dims[-1]))
+    for i in range(num_slices):
+        begins = [0, 0, 0, 0]
+        ends = [dims[0], dims[1], dims[2], dims[3]]
+        begins[slice_dim] = i * slice_size
+        ends[slice_dim] = (i + 1) * slice_size
+        this_torch_output = padded_torch_input[begins[0] : ends[0], begins[1] : ends[1], begins[2] : ends[2]]
+        output_shape = this_torch_output.shape
+        output_shape = [1, 1, output_shape[0] * output_shape[1] * output_shape[2], round_up(output_shape[3], 32)]
+
+        memory_config = ttnn._ttnn.operations.conv.create_sharded_memory_config_from_parallel_config(
+            output_shape, parallel_config, 1
+        )
+        this_ttnn_output = ttnn.padded_slice(ttnn_input, begins, ends, strides, memory_config=memory_config)
+        output = ttnn.to_torch(this_ttnn_output)
+        output = torch.reshape(output, this_torch_output.shape)
+        assert_with_pcc(this_torch_output, output, 0.9999)
path: tests/ttnn/unit_tests/operations/test_slice.py
user: login: s-jovic
body: Can we use exact comparison here?
created_at: 2025-05-19T10:12:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1077,3 +1079,97 @@ def test_ttnn_slice_whisper(
 
         ttnn_output = ttnn.to_torch(ttnn_output)
         assert_with_pcc(torch_output, ttnn_output, 0.999)
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, cores",
+    [
+        [[2, 256, 256, 56], 128, 16],
+        [[2, 256, 128, 23], 16, 8],
+        [[2, 256, 256, 37], 64, 64],
+        [[2, 8, 8, 32], 2, 4],
+        [[2, 8, 16, 2], 2, 8],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_height_sharded_for_conv2d(device, dims, slice_dim, slice_size, cores, layout, orientation):
+    core_grid = device.compute_with_storage_grid_size()
+    if core_grid.x * core_grid.y < cores:
+        pytest.skip("Device does not have enough cores")
+
+    strides = [1, 1, 1, 1]
+    torch.manual_seed(2005)
+    torch_input = torch.randint(-10, 10, dims)
+    core_range = ttnn.num_cores_to_corerangeset(cores, core_grid, orientation == ttnn.ShardOrientation.ROW_MAJOR)
+    num_slices = dims[slice_dim] // slice_size
+    ttnn_input = ttnn.from_torch(
+        torch_input, device=device, layout=layout, dtype=ttnn.bfloat16, memory_config=ttnn.DRAM_MEMORY_CONFIG
+    )
+    parallel_config = ttnn.SlidingWindowParallelConfig(
+        grid=core_range, shard_scheme=ttnn.TensorMemoryLayout.HEIGHT_SHARDED, shard_orientation=orientation
+    )
+    padded_channels = round_up(dims[-1], 32)
+    padded_torch_input = torch.nn.functional.pad(torch_input, (0, padded_channels - dims[-1]))
+    for i in range(num_slices):
+        begins = [0, 0, 0, 0]
+        ends = [dims[0], dims[1], dims[2], dims[3]]
+        begins[slice_dim] = i * slice_size
+        ends[slice_dim] = (i + 1) * slice_size
+        this_torch_output = padded_torch_input[begins[0] : ends[0], begins[1] : ends[1], begins[2] : ends[2]]
+        output_shape = this_torch_output.shape
+        output_shape = [1, 1, output_shape[0] * output_shape[1] * output_shape[2], round_up(output_shape[3], 32)]
+
+        memory_config = ttnn._ttnn.operations.conv.create_sharded_memory_config_from_parallel_config(
+            output_shape, parallel_config, 1
+        )
+        this_ttnn_output = ttnn.padded_slice(ttnn_input, begins, ends, strides, memory_config=memory_config)
+        output = ttnn.to_torch(this_ttnn_output)
+        output = torch.reshape(output, this_torch_output.shape)
+        assert_with_pcc(this_torch_output, output, 0.9999)
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, core_x, core_y",
+    [
+        [[2, 64, 64, 256], 32, 4, 4],
+        [[2, 64, 64, 512], 16, 4, 4],
+        [[2, 16, 16, 1024], 4, 4, 4],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_block_sharded_for_conv2d(device, dims, slice_dim, slice_size, core_x, core_y, layout, orientation):
+    core_grid = device.core_grid
+    if core_grid.x < core_x or core_grid.y < core_y:
+        pytest.skip("Device does not have enough cores")
path: tests/ttnn/unit_tests/operations/test_slice.py
user: login: s-jovic
body: Can you be more descriptive, to include op name and the needed size
created_at: 2025-05-19T10:27:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,141 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(
+            this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i],
+            "Starts {} must be less than the shape of the tensor {} at index {}",
+            this->padded_slice_start[i],
+            input_tensor_a.get_padded_shape()[i],
+            i);
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(
+            this->padded_slice_start[i] <= this->padded_slice_end[i],
+            "Slice start {} must be less than or equal to the end {}",
+            this->padded_slice_start[i],
+            this->padded_slice_end[i]);
+    }
+    if (!output_tensors.empty() && output_tensors[0].has_value()) {
+        const auto output_shape_required = compute_output_specs(input_tensors)[0].logical_shape();
+        const auto& out_tensor = output_tensors[0].value();
+        TT_FATAL(
+            out_tensor.get_padded_shape() == output_shape_required,
+            "The input tensors need a shape of {}, however the output tensor is only {}",
+            output_shape_required,
+            out_tensor.get_padded_shape());
+    }
+    auto output_tensor_shape = this->compute_output_specs(input_tensors)[0].logical_shape();
+    TT_FATAL(!has_step, "Padded slice does not support strided slices");
+}
+
+std::vector<ttnn::TensorSpec> PaddedSliceDeviceOperation::compute_output_specs(
+    const std::vector<Tensor>& input_tensors) const {
+    const auto& input_tensor = input_tensors[0];
+    SmallVector<uint32_t> out_shape(input_tensor.get_logical_shape().rank());
+
+    auto output_dim_i = [this](size_t i) {
+        return (this->padded_slice_end[i] - this->padded_slice_start[i] + this->step[i] - 1) / this->step[i];
+    };
+    for (uint32_t i = 0; i < out_shape.size(); i++) {
+        out_shape[i] = output_dim_i(i);
+    }
+    out_shape[2] = out_shape[0] * out_shape[1] * out_shape[2];
+    out_shape[0] = 1;
+    out_shape[1] = 1;
+
+    if (this->output_mem_config.memory_layout() == TensorMemoryLayout::HEIGHT_SHARDED) {
+        auto output_shard_shape = this->output_mem_config.shard_spec().value().shape;
+        out_shape[out_shape.size() - 1] = output_shard_shape[1];
+    }
+
+    ttnn::Shape output_tensor_shape(std::move(out_shape));
+    auto tensor_layout = TensorLayout::fromPaddedShape(
+        input_tensor.get_dtype(),
+        PageConfig(Layout::ROW_MAJOR),
+        this->output_mem_config,
+        output_tensor_shape,
+        output_tensor_shape);
+    return {ttnn::TensorSpec(output_tensor_shape, tensor_layout)};
+
+    // tt::tt_metal::TensorLayout(
+    // input_tensor.get_dtype(), PageConfig(input_tensor.get_layout()), this->output_mem_config))};
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: s-jovic
body: remove commented code
created_at: 2025-05-19T10:30:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,141 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(
+            this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i],
+            "Starts {} must be less than the shape of the tensor {} at index {}",
+            this->padded_slice_start[i],
+            input_tensor_a.get_padded_shape()[i],
+            i);
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(
+            this->padded_slice_start[i] <= this->padded_slice_end[i],
+            "Slice start {} must be less than or equal to the end {}",
+            this->padded_slice_start[i],
+            this->padded_slice_end[i]);
+    }
+    if (!output_tensors.empty() && output_tensors[0].has_value()) {
+        const auto output_shape_required = compute_output_specs(input_tensors)[0].logical_shape();
+        const auto& out_tensor = output_tensors[0].value();
+        TT_FATAL(
+            out_tensor.get_padded_shape() == output_shape_required,
+            "The input tensors need a shape of {}, however the output tensor is only {}",
+            output_shape_required,
+            out_tensor.get_padded_shape());
+    }
+    auto output_tensor_shape = this->compute_output_specs(input_tensors)[0].logical_shape();
+    TT_FATAL(!has_step, "Padded slice does not support strided slices");
+}
+
+std::vector<ttnn::TensorSpec> PaddedSliceDeviceOperation::compute_output_specs(
+    const std::vector<Tensor>& input_tensors) const {
+    const auto& input_tensor = input_tensors[0];
+    SmallVector<uint32_t> out_shape(input_tensor.get_logical_shape().rank());
+
+    auto output_dim_i = [this](size_t i) {
+        return (this->padded_slice_end[i] - this->padded_slice_start[i] + this->step[i] - 1) / this->step[i];
+    };
+    for (uint32_t i = 0; i < out_shape.size(); i++) {
+        out_shape[i] = output_dim_i(i);
+    }
+    out_shape[2] = out_shape[0] * out_shape[1] * out_shape[2];
+    out_shape[0] = 1;
+    out_shape[1] = 1;
+
+    if (this->output_mem_config.memory_layout() == TensorMemoryLayout::HEIGHT_SHARDED) {
+        auto output_shard_shape = this->output_mem_config.shard_spec().value().shape;
+        out_shape[out_shape.size() - 1] = output_shard_shape[1];
+    }
+
+    ttnn::Shape output_tensor_shape(std::move(out_shape));
+    auto tensor_layout = TensorLayout::fromPaddedShape(
+        input_tensor.get_dtype(),
+        PageConfig(Layout::ROW_MAJOR),
+        this->output_mem_config,
+        output_tensor_shape,
+        output_tensor_shape);
+    return {ttnn::TensorSpec(output_tensor_shape, tensor_layout)};
+
+    // tt::tt_metal::TensorLayout(
+    // input_tensor.get_dtype(), PageConfig(input_tensor.get_layout()), this->output_mem_config))};
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: Removed
created_at: 2025-05-20T05:29:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1077,3 +1079,97 @@ def test_ttnn_slice_whisper(
 
         ttnn_output = ttnn.to_torch(ttnn_output)
         assert_with_pcc(torch_output, ttnn_output, 0.999)
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, cores",
+    [
+        [[2, 256, 256, 56], 128, 16],
+        [[2, 256, 128, 23], 16, 8],
+        [[2, 256, 256, 37], 64, 64],
+        [[2, 8, 8, 32], 2, 4],
+        [[2, 8, 16, 2], 2, 8],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_height_sharded_for_conv2d(device, dims, slice_dim, slice_size, cores, layout, orientation):
+    core_grid = device.compute_with_storage_grid_size()
+    if core_grid.x * core_grid.y < cores:
+        pytest.skip("Device does not have enough cores")
+
+    strides = [1, 1, 1, 1]
+    torch.manual_seed(2005)
+    torch_input = torch.randint(-10, 10, dims)
+    core_range = ttnn.num_cores_to_corerangeset(cores, core_grid, orientation == ttnn.ShardOrientation.ROW_MAJOR)
+    num_slices = dims[slice_dim] // slice_size
+    ttnn_input = ttnn.from_torch(
+        torch_input, device=device, layout=layout, dtype=ttnn.bfloat16, memory_config=ttnn.DRAM_MEMORY_CONFIG
+    )
+    parallel_config = ttnn.SlidingWindowParallelConfig(
+        grid=core_range, shard_scheme=ttnn.TensorMemoryLayout.HEIGHT_SHARDED, shard_orientation=orientation
+    )
+    padded_channels = round_up(dims[-1], 32)
+    padded_torch_input = torch.nn.functional.pad(torch_input, (0, padded_channels - dims[-1]))
+    for i in range(num_slices):
+        begins = [0, 0, 0, 0]
+        ends = [dims[0], dims[1], dims[2], dims[3]]
+        begins[slice_dim] = i * slice_size
+        ends[slice_dim] = (i + 1) * slice_size
+        this_torch_output = padded_torch_input[begins[0] : ends[0], begins[1] : ends[1], begins[2] : ends[2]]
+        output_shape = this_torch_output.shape
+        output_shape = [1, 1, output_shape[0] * output_shape[1] * output_shape[2], round_up(output_shape[3], 32)]
+
+        memory_config = ttnn._ttnn.operations.conv.create_sharded_memory_config_from_parallel_config(
+            output_shape, parallel_config, 1
+        )
+        this_ttnn_output = ttnn.padded_slice(ttnn_input, begins, ends, strides, memory_config=memory_config)
+        output = ttnn.to_torch(this_ttnn_output)
+        output = torch.reshape(output, this_torch_output.shape)
+        assert_with_pcc(this_torch_output, output, 0.9999)
path: tests/ttnn/unit_tests/operations/test_slice.py
user: login: sankarmanoj-tt
body: Yes. Good idea!
created_at: 2025-05-20T05:29:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "padded_slice_pybind.hpp"
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include "cpp/ttnn-pybind/decorators.hpp"
+
+namespace ttnn::operations::experimental::padded_slice {
+namespace py = pybind11;
+
+void bind_padded_slice(py::module& module) {
+    auto doc =
+        R"doc(
+            Returns a padded_sliced tensor. If the input tensor is on host, the padded_slice will be performed on host, and if its on device it will be performed on device.
+
+            Args:
+                input_tensor: Input Tensor.
+                padded_slice_start: Start indices of input tensor. Values along each dim must be < input_tensor_shape[i].
+                padded_slice_end: End indices of input tensor. Values along each dim must be < input_tensor_shape[i].
+                padded_slice_step: (Optional[List[int[tensor rank]]) Step size for each dim. Default is None, which works out be 1 for each dimension.
+
+            Keyword Args:
+                memory_config Memory Config of the output tensor
+                queue_id (uint8, optional) command queue id
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice_pybind.cpp
user: login: sankarmanoj-tt
body: Fixed. 
created_at: 2025-05-20T05:29:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1077,3 +1079,97 @@ def test_ttnn_slice_whisper(
 
         ttnn_output = ttnn.to_torch(ttnn_output)
         assert_with_pcc(torch_output, ttnn_output, 0.999)
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, cores",
+    [
+        [[2, 256, 256, 56], 128, 16],
+        [[2, 256, 128, 23], 16, 8],
+        [[2, 256, 256, 37], 64, 64],
+        [[2, 8, 8, 32], 2, 4],
+        [[2, 8, 16, 2], 2, 8],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_height_sharded_for_conv2d(device, dims, slice_dim, slice_size, cores, layout, orientation):
+    core_grid = device.compute_with_storage_grid_size()
+    if core_grid.x * core_grid.y < cores:
+        pytest.skip("Device does not have enough cores")
+
+    strides = [1, 1, 1, 1]
+    torch.manual_seed(2005)
+    torch_input = torch.randint(-10, 10, dims)
+    core_range = ttnn.num_cores_to_corerangeset(cores, core_grid, orientation == ttnn.ShardOrientation.ROW_MAJOR)
+    num_slices = dims[slice_dim] // slice_size
+    ttnn_input = ttnn.from_torch(
+        torch_input, device=device, layout=layout, dtype=ttnn.bfloat16, memory_config=ttnn.DRAM_MEMORY_CONFIG
+    )
+    parallel_config = ttnn.SlidingWindowParallelConfig(
+        grid=core_range, shard_scheme=ttnn.TensorMemoryLayout.HEIGHT_SHARDED, shard_orientation=orientation
+    )
+    padded_channels = round_up(dims[-1], 32)
+    padded_torch_input = torch.nn.functional.pad(torch_input, (0, padded_channels - dims[-1]))
+    for i in range(num_slices):
+        begins = [0, 0, 0, 0]
+        ends = [dims[0], dims[1], dims[2], dims[3]]
+        begins[slice_dim] = i * slice_size
+        ends[slice_dim] = (i + 1) * slice_size
+        this_torch_output = padded_torch_input[begins[0] : ends[0], begins[1] : ends[1], begins[2] : ends[2]]
+        output_shape = this_torch_output.shape
+        output_shape = [1, 1, output_shape[0] * output_shape[1] * output_shape[2], round_up(output_shape[3], 32)]
+
+        memory_config = ttnn._ttnn.operations.conv.create_sharded_memory_config_from_parallel_config(
+            output_shape, parallel_config, 1
+        )
+        this_ttnn_output = ttnn.padded_slice(ttnn_input, begins, ends, strides, memory_config=memory_config)
+        output = ttnn.to_torch(this_ttnn_output)
+        output = torch.reshape(output, this_torch_output.shape)
+        assert_with_pcc(this_torch_output, output, 0.9999)
+
+
+@pytest.mark.parametrize(
+    "dims, slice_size, core_x, core_y",
+    [
+        [[2, 64, 64, 256], 32, 4, 4],
+        [[2, 64, 64, 512], 16, 4, 4],
+        [[2, 16, 16, 1024], 4, 4, 4],
+    ],
+)
+@pytest.mark.parametrize("slice_dim", [1, 2])
+@pytest.mark.parametrize("layout", [ttnn.ROW_MAJOR_LAYOUT])
+@pytest.mark.parametrize("orientation", [ttnn.ShardOrientation.ROW_MAJOR, ttnn.ShardOrientation.COL_MAJOR])
+def test_slice_block_sharded_for_conv2d(device, dims, slice_dim, slice_size, core_x, core_y, layout, orientation):
+    core_grid = device.core_grid
+    if core_grid.x < core_x or core_grid.y < core_y:
+        pytest.skip("Device does not have enough cores")
path: tests/ttnn/unit_tests/operations/test_slice.py
user: login: sankarmanoj-tt
body: Done
created_at: 2025-05-20T05:39:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdint.h>
+#include <cstdint>
+#include "dataflow_api.h"
+#include "hw/inc/dataflow_api.h"
+#include "hw/inc/dataflow_api_addrgen.h"
+#include "debug/dprint.h"
+void kernel_main() {
+    const uint32_t num_units = get_arg_val<uint32_t>(0);
+    const uint32_t num_elements_per_row = get_arg_val<uint32_t>(1);
+    const uint32_t unpadded_row_size_bytes = get_arg_val<uint32_t>(2);
+    const uint32_t padded_row_size_bytes = get_arg_val<uint32_t>(3);
+    const uint32_t pad_size_bytes = padded_row_size_bytes - unpadded_row_size_bytes;
+    constexpr uint32_t cb_id_out = get_compile_time_arg_val(0);
+    constexpr uint32_t cb_temp_pad = get_compile_time_arg_val(2);
+
+#ifdef DEBUG
+    DPRINT << "num_units: " << num_units << ", num_elements_per_row: " << num_elements_per_row
+           << ", unpadded_row_size_bytes: " << unpadded_row_size_bytes
+           << ", padded_row_size_bytes: " << padded_row_size_bytes << ", pad_size_bytes: " << pad_size_bytes << ENDL();
+#endif
+    const uint32_t pad_addr = get_read_ptr(cb_temp_pad);
+    const uint32_t out_addr = get_read_ptr(cb_id_out);
+    volatile tt_l1_ptr uint16_t* pad_ptr = reinterpret_cast<volatile tt_l1_ptr uint16_t*>(pad_addr);
+    for (uint32_t i = 0; i < num_elements_per_row; ++i) {
+        pad_ptr[i] = 0;  // Changing this to any other value causes a hang. Figure out why.
+    }
+    uint32_t write_addr = out_addr + unpadded_row_size_bytes;
+    uint64_t pad_noc_addr = get_noc_addr(pad_addr + unpadded_row_size_bytes);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/kernels/dataflow/writer_unary_sharded_padded.cpp
user: login: sankarmanoj-tt
body: This code is very simple, and would not need something like ShardedAddrGen
created_at: 2025-05-21T06:47:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <optional>
+
+#include "ttnn/tensor/tensor.hpp"
+#include "ttnn/run_operation.hpp"
+
+namespace ttnn::operations::experimental {
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.hpp
user: login: sminakov-tt
body: Let's remove this function from the header and move it into `ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp`
created_at: 2025-05-21T06:58:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <optional>
+
+#include "ttnn/tensor/tensor.hpp"
+#include "ttnn/run_operation.hpp"
+
+namespace ttnn::operations::experimental {
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start);
+uint32_t get_tiled_start_offset(const Tensor& input_tensor, const ttnn::Shape& padded_slice_start);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.hpp
user: login: sminakov-tt
body: There are no other mentions of `get_tiled_start_offset`, was it left here by mistake?
created_at: 2025-05-21T06:58:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sminakov-tt
body: Please remove `inline __attribute__((always_inline))` (here and in the function below). Compiler generally understands what to inline without extra hints. If you believe it's not the case here, please provide a benchmark.
After removing the inline, please put those functions into `namespace { namespace CMAKE_UNIQUE_NAMESPACE {`
created_at: 2025-05-21T07:01:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <optional>
+
+#include "ttnn/tensor/tensor.hpp"
+#include "ttnn/run_operation.hpp"
+
+namespace ttnn::operations::experimental {
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start);
+uint32_t get_tiled_start_offset(const Tensor& input_tensor, const ttnn::Shape& padded_slice_start);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.hpp
user: login: sankarmanoj-tt
body: Yes. I've removed it. 
created_at: 2025-05-21T07:01:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <optional>
+
+#include "ttnn/tensor/tensor.hpp"
+#include "ttnn/run_operation.hpp"
+
+namespace ttnn::operations::experimental {
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.hpp
user: login: sankarmanoj-tt
body: I'll move it there and make it static.
created_at: 2025-05-21T07:02:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(
+            this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i],
+            "Starts {} must be less than the shape of the tensor {} at index {}",
+            this->padded_slice_start[i],
+            input_tensor_a.get_padded_shape()[i],
+            i);
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(
+            this->padded_slice_start[i] <= this->padded_slice_end[i],
+            "Slice start {} must be less than or equal to the end {}",
+            this->padded_slice_start[i],
+            this->padded_slice_end[i]);
+    }
+    if (!output_tensors.empty() && output_tensors[0].has_value()) {
+        const auto output_shape_required = compute_output_specs(input_tensors)[0].logical_shape();
+        const auto& out_tensor = output_tensors[0].value();
+        TT_FATAL(
+            out_tensor.get_padded_shape() == output_shape_required,
+            "The input tensors need a shape of {}, however the output tensor is only {}",
+            output_shape_required,
+            out_tensor.get_padded_shape());
+    }
+    auto output_tensor_shape = this->compute_output_specs(input_tensors)[0].logical_shape();
+    TT_FATAL(!has_step, "Padded slice does not support strided slices");
+}
+
+std::vector<ttnn::TensorSpec> PaddedSliceDeviceOperation::compute_output_specs(
+    const std::vector<Tensor>& input_tensors) const {
+    const auto& input_tensor = input_tensors[0];
+    SmallVector<uint32_t> out_shape(input_tensor.get_logical_shape().rank());
+
+    auto output_dim_i = [this](size_t i) {
+        return (this->padded_slice_end[i] - this->padded_slice_start[i] + this->step[i] - 1) / this->step[i];
+    };
+    for (uint32_t i = 0; i < out_shape.size(); i++) {
+        out_shape[i] = output_dim_i(i);
+    }
+    out_shape[2] = out_shape[0] * out_shape[1] * out_shape[2];
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sminakov-tt
body: Here you're explicitly assuming that input_tensor rank is at least 3, which might not be the case
created_at: 2025-05-21T07:02:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sminakov-tt
body: Please provide more descriptive messages than "Error"
created_at: 2025-05-21T07:03:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sminakov-tt
body: This is likely invalid for tensors with ranks < 2
created_at: 2025-05-21T07:04:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sminakov-tt
body: Please avoid hard-coding page sizes and manual calculations, you can query those from Tensor itself
created_at: 2025-05-21T07:07:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sminakov-tt
body: Underflows for rank < 2
created_at: 2025-05-21T07:07:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(
+            this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i],
+            "Starts {} must be less than the shape of the tensor {} at index {}",
+            this->padded_slice_start[i],
+            input_tensor_a.get_padded_shape()[i],
+            i);
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(
+            this->padded_slice_start[i] <= this->padded_slice_end[i],
+            "Slice start {} must be less than or equal to the end {}",
+            this->padded_slice_start[i],
+            this->padded_slice_end[i]);
+    }
+    if (!output_tensors.empty() && output_tensors[0].has_value()) {
+        const auto output_shape_required = compute_output_specs(input_tensors)[0].logical_shape();
+        const auto& out_tensor = output_tensors[0].value();
+        TT_FATAL(
+            out_tensor.get_padded_shape() == output_shape_required,
+            "The input tensors need a shape of {}, however the output tensor is only {}",
+            output_shape_required,
+            out_tensor.get_padded_shape());
+    }
+    auto output_tensor_shape = this->compute_output_specs(input_tensors)[0].logical_shape();
+    TT_FATAL(!has_step, "Padded slice does not support strided slices");
+}
+
+std::vector<ttnn::TensorSpec> PaddedSliceDeviceOperation::compute_output_specs(
+    const std::vector<Tensor>& input_tensors) const {
+    const auto& input_tensor = input_tensors[0];
+    SmallVector<uint32_t> out_shape(input_tensor.get_logical_shape().rank());
+
+    auto output_dim_i = [this](size_t i) {
+        return (this->padded_slice_end[i] - this->padded_slice_start[i] + this->step[i] - 1) / this->step[i];
+    };
+    for (uint32_t i = 0; i < out_shape.size(); i++) {
+        out_shape[i] = output_dim_i(i);
+    }
+    out_shape[2] = out_shape[0] * out_shape[1] * out_shape[2];
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: The op currently only supports rank 4 Tensors. 
created_at: 2025-05-21T07:09:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: Done. 
created_at: 2025-05-21T07:09:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: This op is only for rank 4 Tensors. 
created_at: 2025-05-21T07:09:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,369 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    tt::log_info("Input Buffer : {}", input_tensor.get_storage());
+
+    auto num_cores_total = cores.size();
+
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+    bool is_block_sharded = output_tensor.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+    uint32_t num_cores_channels = 1;
+    auto total_cores = output_shard_spec.grid;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+    uint32_t input_page_size = input_shape[-1] * input_tensor.element_size();
+    uint32_t input_row_size_bytes = input_shape[-1] * input_tensor.element_size() / num_cores_channels;
+
+    uint32_t output_row_size_bytes = output_shard_shape[1] * input_tensor.element_size();
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sminakov-tt
body: Please avoid accessing shape with positive constant indexes throughout the PR. This code fails for tensors with small ranks
created_at: 2025-05-21T07:10:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,369 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    tt::log_info("Input Buffer : {}", input_tensor.get_storage());
+
+    auto num_cores_total = cores.size();
+
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+    bool is_block_sharded = output_tensor.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+    uint32_t num_cores_channels = 1;
+    auto total_cores = output_shard_spec.grid;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+    uint32_t input_page_size = input_shape[-1] * input_tensor.element_size();
+    uint32_t input_row_size_bytes = input_shape[-1] * input_tensor.element_size() / num_cores_channels;
+
+    uint32_t output_row_size_bytes = output_shard_shape[1] * input_tensor.element_size();
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sankarmanoj-tt
body: Shard shape would always be 2D. 
created_at: 2025-05-21T07:12:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "ttnn/decorators.hpp"
+
+namespace ttnn {
+namespace operations {
+namespace experimental {
+
+struct PaddedSliceOperation {
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        tt::stl::Span<const T> begins,
+        tt::stl::Span<const T> ends,
+        tt::stl::Span<const T> step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        const ttnn::Tensor& input_tensor,
+        tt::stl::Span<const T> output_tensor_start,
+        tt::stl::Span<const T> output_tensor_end,
+        tt::stl::Span<const T> step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const ttnn::SmallVector<T>& begins,
+        const ttnn::SmallVector<T>& ends,
+        const ttnn::SmallVector<T>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt) {
+        return invoke(
+            queue_id,
+            input_tensor,
+            tt::stl::Span<const T>(begins),
+            tt::stl::Span<const T>(ends),
+            tt::stl::Span<const T>(step),
+            memory_config_arg,
+            optional_output_tensor,
+            pad_value);
+    }
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        const ttnn::Tensor& input_tensor,
+        const ttnn::SmallVector<T>& begins,
+        const ttnn::SmallVector<T>& ends,
+        const ttnn::SmallVector<T>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt) {
+        return invoke(
+            input_tensor,
+            tt::stl::Span<const T>(begins),
+            tt::stl::Span<const T>(ends),
+            tt::stl::Span<const T>(step),
+            memory_config_arg,
+            optional_output_tensor,
+            pad_value);
+    }
+
+    template <typename T, std::size_t N>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const std::array<T, N>& output_tensor_start,
+        const std::array<T, N>& output_tensor_end,
+        const std::array<T, N>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T, std::size_t N>
+    static ttnn::Tensor invoke(
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.hpp
user: login: sminakov-tt
body: Can we please remove extra overloads for `array<T, N>`? An overload with a span already covers it, and with template instantiation in the .cpp it doesn't actually support arbitrary N
created_at: 2025-05-21T07:13:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(
+            this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i],
+            "Starts {} must be less than the shape of the tensor {} at index {}",
+            this->padded_slice_start[i],
+            input_tensor_a.get_padded_shape()[i],
+            i);
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(
+            this->padded_slice_start[i] <= this->padded_slice_end[i],
+            "Slice start {} must be less than or equal to the end {}",
+            this->padded_slice_start[i],
+            this->padded_slice_end[i]);
+    }
+    if (!output_tensors.empty() && output_tensors[0].has_value()) {
+        const auto output_shape_required = compute_output_specs(input_tensors)[0].logical_shape();
+        const auto& out_tensor = output_tensors[0].value();
+        TT_FATAL(
+            out_tensor.get_padded_shape() == output_shape_required,
+            "The input tensors need a shape of {}, however the output tensor is only {}",
+            output_shape_required,
+            out_tensor.get_padded_shape());
+    }
+    auto output_tensor_shape = this->compute_output_specs(input_tensors)[0].logical_shape();
+    TT_FATAL(!has_step, "Padded slice does not support strided slices");
+}
+
+std::vector<ttnn::TensorSpec> PaddedSliceDeviceOperation::compute_output_specs(
+    const std::vector<Tensor>& input_tensors) const {
+    const auto& input_tensor = input_tensors[0];
+    SmallVector<uint32_t> out_shape(input_tensor.get_logical_shape().rank());
+
+    auto output_dim_i = [this](size_t i) {
+        return (this->padded_slice_end[i] - this->padded_slice_start[i] + this->step[i] - 1) / this->step[i];
+    };
+    for (uint32_t i = 0; i < out_shape.size(); i++) {
+        out_shape[i] = output_dim_i(i);
+    }
+    out_shape[2] = out_shape[0] * out_shape[1] * out_shape[2];
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sminakov-tt
body: Is this a fundamental limitation (like it wouldn't just work if we use negative indexes everywhere)?
If the OP doesn't support tensors with rank != 4, please add a check for this in the validate method
created_at: 2025-05-21T07:16:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "ttnn/decorators.hpp"
+
+namespace ttnn {
+namespace operations {
+namespace experimental {
+
+struct PaddedSliceOperation {
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        tt::stl::Span<const T> begins,
+        tt::stl::Span<const T> ends,
+        tt::stl::Span<const T> step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        const ttnn::Tensor& input_tensor,
+        tt::stl::Span<const T> output_tensor_start,
+        tt::stl::Span<const T> output_tensor_end,
+        tt::stl::Span<const T> step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const ttnn::SmallVector<T>& begins,
+        const ttnn::SmallVector<T>& ends,
+        const ttnn::SmallVector<T>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt) {
+        return invoke(
+            queue_id,
+            input_tensor,
+            tt::stl::Span<const T>(begins),
+            tt::stl::Span<const T>(ends),
+            tt::stl::Span<const T>(step),
+            memory_config_arg,
+            optional_output_tensor,
+            pad_value);
+    }
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        const ttnn::Tensor& input_tensor,
+        const ttnn::SmallVector<T>& begins,
+        const ttnn::SmallVector<T>& ends,
+        const ttnn::SmallVector<T>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt) {
+        return invoke(
+            input_tensor,
+            tt::stl::Span<const T>(begins),
+            tt::stl::Span<const T>(ends),
+            tt::stl::Span<const T>(step),
+            memory_config_arg,
+            optional_output_tensor,
+            pad_value);
+    }
+
+    template <typename T, std::size_t N>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const std::array<T, N>& output_tensor_start,
+        const std::array<T, N>& output_tensor_end,
+        const std::array<T, N>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T, std::size_t N>
+    static ttnn::Tensor invoke(
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.hpp
user: login: sankarmanoj-tt
body: Done. 
created_at: 2025-05-21T07:24:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Error");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(
+            this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i],
+            "Starts {} must be less than the shape of the tensor {} at index {}",
+            this->padded_slice_start[i],
+            input_tensor_a.get_padded_shape()[i],
+            i);
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(
+            this->padded_slice_start[i] <= this->padded_slice_end[i],
+            "Slice start {} must be less than or equal to the end {}",
+            this->padded_slice_start[i],
+            this->padded_slice_end[i]);
+    }
+    if (!output_tensors.empty() && output_tensors[0].has_value()) {
+        const auto output_shape_required = compute_output_specs(input_tensors)[0].logical_shape();
+        const auto& out_tensor = output_tensors[0].value();
+        TT_FATAL(
+            out_tensor.get_padded_shape() == output_shape_required,
+            "The input tensors need a shape of {}, however the output tensor is only {}",
+            output_shape_required,
+            out_tensor.get_padded_shape());
+    }
+    auto output_tensor_shape = this->compute_output_specs(input_tensors)[0].logical_shape();
+    TT_FATAL(!has_step, "Padded slice does not support strided slices");
+}
+
+std::vector<ttnn::TensorSpec> PaddedSliceDeviceOperation::compute_output_specs(
+    const std::vector<Tensor>& input_tensors) const {
+    const auto& input_tensor = input_tensors[0];
+    SmallVector<uint32_t> out_shape(input_tensor.get_logical_shape().rank());
+
+    auto output_dim_i = [this](size_t i) {
+        return (this->padded_slice_end[i] - this->padded_slice_start[i] + this->step[i] - 1) / this->step[i];
+    };
+    for (uint32_t i = 0; i < out_shape.size(); i++) {
+        out_shape[i] = output_dim_i(i);
+    }
+    out_shape[2] = out_shape[0] * out_shape[1] * out_shape[2];
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: I've added checks to ensure rank is always 4. 
created_at: 2025-05-21T07:25:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: How do I get num_pages from the Tensor?
created_at: 2025-05-21T07:26:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+inline __attribute__((always_inline)) uint32_t
+get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: The slice op uses the same code. Both could be updated as a different PR. 
created_at: 2025-05-21T07:26:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+inline __attribute__((always_inline)) uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: I've moved the function to the same file where it's used and made it static. Is that enough?
created_at: 2025-05-21T13:59:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -47,11 +47,12 @@
 #include "ttnn/operations/experimental/unary_backward/gelu_backward/gelu_backward_pybind.hpp"
 #include "ttnn/operations/experimental/reduction/sort/sort_pybind.hpp"
 #include "ttnn/operations/experimental/gather/gather_pybind.hpp"
-
+#include "ttnn/operations/experimental/padded_slice/padded_slice_pybind.hpp"
 namespace ttnn::operations::experimental {
path: ttnn/cpp/ttnn/operations/experimental/experimental_pybind.cpp
user: login: sminakov-tt
body: Please add a newline after include
created_at: 2025-05-22T04:57:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(input_tensor_a.get_padded_shape().rank() == 4, "Only 4D tensors are supported for padded_slice");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Padded slice start, end and input tensor must all have the same rank");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(
+            this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i],
+            "Starts {} must be less than the shape of the tensor {} at index {}",
+            this->padded_slice_start[i],
+            input_tensor_a.get_padded_shape()[i],
+            i);
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(
+            this->padded_slice_start[i] <= this->padded_slice_end[i],
+            "Slice start {} must be less than or equal to the end {}",
+            this->padded_slice_start[i],
+            this->padded_slice_end[i]);
+    }
+    if (!output_tensors.empty() && output_tensors[0].has_value()) {
+        const auto output_shape_required = compute_output_specs(input_tensors)[0].logical_shape();
+        const auto& out_tensor = output_tensors[0].value();
+        TT_FATAL(
+            out_tensor.get_padded_shape() == output_shape_required,
+            "The input tensors need a shape of {}, however the output tensor is only {}",
+            output_shape_required,
+            out_tensor.get_padded_shape());
+    }
+    auto output_tensor_shape = this->compute_output_specs(input_tensors)[0].logical_shape();
+    TT_FATAL(!has_step, "Padded slice does not support strided slices");
+}
+
+std::vector<ttnn::TensorSpec> PaddedSliceDeviceOperation::compute_output_specs(
+    const std::vector<Tensor>& input_tensors) const {
+    const auto& input_tensor = input_tensors[0];
+    SmallVector<uint32_t> out_shape(input_tensor.get_logical_shape().rank());
+
+    auto output_dim_i = [this](size_t i) {
+        return (this->padded_slice_end[i] - this->padded_slice_start[i] + this->step[i] - 1) / this->step[i];
+    };
+    for (uint32_t i = 0; i < out_shape.size(); i++) {
+        out_shape[i] = output_dim_i(i);
+    }
+    out_shape[2] = out_shape[0] * out_shape[1] * out_shape[2];
+    out_shape[0] = 1;
+    out_shape[1] = 1;
+
+    if (this->output_mem_config.memory_layout() == TensorMemoryLayout::HEIGHT_SHARDED) {
+        auto output_shard_shape = this->output_mem_config.shard_spec().value().shape;
+        out_shape[out_shape.size() - 1] = output_shard_shape[1];
+    }
+
+    ttnn::Shape output_tensor_shape(std::move(out_shape));
+    auto tensor_layout = TensorLayout::fromPaddedShape(
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sminakov-tt
body: It seems like the usage of TensorLayout::fromPaddedShape is redundant here, you can use a regular constructor instead and avoid passing output_tensor_shape twice
created_at: 2025-05-22T04:59:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,218 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "device/padded_slice_op.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/run_operation.hpp"
+#include "ttnn/common/constants.hpp"
+#include "ttnn/common/queue_id.hpp"
+#include "ttnn/operations/creation.hpp"
+#include "ttnn/operations/core/core.hpp"
+#include "ttnn/operations/data_movement/common/common.hpp"
+#include "ttnn/operations/data_movement/fill_pad/fill_pad.hpp"
+#include "ttnn/operations/experimental/reshape/view.hpp"
+#include "ttnn/tensor/types.hpp"
+#include "padded_slice.hpp"
+
+namespace ttnn::operations::experimental {
+
+template <typename T>
+ttnn::Tensor PaddedSliceOperation::invoke(
+    QueueId queue_id,
+    const ttnn::Tensor& input_tensor,
+    tt::stl::Span<const T> begins,
+    tt::stl::Span<const T> ends,
+    tt::stl::Span<const T> step,
+    const MemoryConfig& memory_config,
+    const std::optional<Tensor>& optional_output_tensor,
+    const std::optional<float>& pad_value) {
+    // Ensure start and end vectors have matching sizes and correct tensor rank
+
+    const auto& input_shape = input_tensor.get_logical_shape();
+    uint32_t input_rank = input_shape.rank();
+    auto input_layout = input_tensor.get_layout();
+
+    TT_FATAL(input_rank == 4, "Only 4D tensors are supported for padded_slice");
+
+    if (input_rank == 0) {
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.cpp
user: login: sminakov-tt
body: This check for input_rank == 0 is always false because of TT_FATAL(input_rank == 4)
created_at: 2025-05-22T05:00:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,218 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "device/padded_slice_op.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/run_operation.hpp"
+#include "ttnn/common/constants.hpp"
+#include "ttnn/common/queue_id.hpp"
+#include "ttnn/operations/creation.hpp"
+#include "ttnn/operations/core/core.hpp"
+#include "ttnn/operations/data_movement/common/common.hpp"
+#include "ttnn/operations/data_movement/fill_pad/fill_pad.hpp"
+#include "ttnn/operations/experimental/reshape/view.hpp"
+#include "ttnn/tensor/types.hpp"
+#include "padded_slice.hpp"
+
+namespace ttnn::operations::experimental {
+
+template <typename T>
+ttnn::Tensor PaddedSliceOperation::invoke(
+    QueueId queue_id,
+    const ttnn::Tensor& input_tensor,
+    tt::stl::Span<const T> begins,
+    tt::stl::Span<const T> ends,
+    tt::stl::Span<const T> step,
+    const MemoryConfig& memory_config,
+    const std::optional<Tensor>& optional_output_tensor,
+    const std::optional<float>& pad_value) {
+    // Ensure start and end vectors have matching sizes and correct tensor rank
+
+    const auto& input_shape = input_tensor.get_logical_shape();
+    uint32_t input_rank = input_shape.rank();
+    auto input_layout = input_tensor.get_layout();
+
+    TT_FATAL(input_rank == 4, "Only 4D tensors are supported for padded_slice");
+
+    if (input_rank == 0) {
+        return input_tensor;
+    }
+    TT_FATAL(
+        input_rank == begins.size(), "Input rank {} and begins {} must have the same size", input_rank, begins.size());
+    TT_FATAL(begins.size() == ends.size(), "Start {} and end {} must have the same size", begins.size(), ends.size());
+    TT_FATAL(
+        step.size() == begins.size(),
+        "Step {} must have the same size as start {} and end",
+        step.size(),
+        begins.size());
+
+    bool no_step = std::ranges::all_of(step, [](uint32_t s) { return s == 1; });
+    bool starts_zero = std::ranges::all_of(begins, [](uint32_t s) { return s == 0; });
+    bool ends_max = true;
+    for (size_t i = 0; i < ends.size(); ++i) {
+        ends_max &= ends[i] == input_shape[i];
+        if (!ends_max) {
+            break;
+        }
+    }
+
+    TT_FATAL(no_step, "Steps != 1 are not supported for padded_slice.");
+    TT_FATAL(input_layout == Layout::ROW_MAJOR, "Only Row Major Inputs are supported for padded_slice.");
+    TT_FATAL(memory_config.is_sharded(), "Output Memory Config must be sharded. Use slice for non-sharded outputs.");
+    TT_FATAL(!input_tensor.memory_config().is_sharded(), " padded_slice does not support sharded inputs.");
+
+    const auto& tile_shape = input_tensor.get_tensor_spec().tile().get_tile_shape();
+
+    auto ret_adjustment([&](const ttnn::Tensor& ret_input_tensor) {
+        if (ret_input_tensor.storage_type() == StorageType::DEVICE) {
+            auto tensor = ttnn::to_memory_config(ret_input_tensor, memory_config, std::nullopt);
+            tensor = ttnn::to_layout(tensor, input_layout, std::nullopt, std::nullopt, (IDevice*)nullptr);
+            return tensor;
+        }
+        return ret_input_tensor;
+    });
+
+    // No-op check
+    if (no_step && starts_zero && ends_max) {
+        return ret_adjustment(input_tensor);
+    }
+
+    // Create modified vectors with wrapped indices and adjust them to match the tensor's rank
+    ttnn::SmallVector<uint32_t> modified_begins(input_rank, 0);
+    ttnn::SmallVector<uint32_t> modified_ends(input_rank, 0);
+    ttnn::SmallVector<uint32_t> modified_step(input_rank, 1);
+
+    // Wrap indices and adjust begins, ends, and step
+    for (size_t i = 0; i < begins.size(); ++i) {
+        if constexpr (std::is_signed_v<T>) {
+            modified_begins[i] = data_movement::wrap_index(begins[i], input_shape[i]);
+            modified_ends[i] = data_movement::wrap_index(ends[i], input_shape[i]);
+            modified_step[i] = static_cast<uint32_t>(step[i]);
+        } else {
+            modified_begins[i] = begins[i];
+            modified_ends[i] = ends[i];
+            modified_step[i] = step[i];
+        }
+    }
+
+    auto output_dim_i = [&modified_begins, &modified_step](size_t i, const ttnn::SmallVector<uint32_t>& modified_ends) {
+        return (modified_ends[i] - modified_begins[i] + modified_step[i] - 1) / modified_step[i];
+    };
+
+    auto check_handled_tile_alignment = [&modified_begins, &input_rank, &tile_shape]() -> bool {
+        return (
+            modified_begins[input_rank - 1] % tile_shape[1] == 0 &&
+            modified_begins[input_rank - 2] % tile_shape[0] == 0);
+    };
+
+    bool rm_only = false;
+    bool one_dimensional = input_rank == 1;
+    bool handled_tile_alignment = one_dimensional ? true : check_handled_tile_alignment();
+
+    ttnn::SmallVector<uint32_t> padded_ends = modified_ends;
+
+    ttnn::SmallVector<uint32_t> actual_shape_vec, final_padded_shape_vec;
+    actual_shape_vec.reserve(input_rank);
+    final_padded_shape_vec.reserve(input_rank);
+    bool empty = false;
+
+    // Compute actual and padded shapes for the original input rank
+    for (size_t i = 0; i < input_rank; ++i) {
+        TT_FATAL(
+            modified_ends[i] >= modified_begins[i],
+            "End {} must be greater than or equal to start {}",
+            modified_ends[i],
+            modified_begins[i]);
+        auto val = output_dim_i(i, modified_ends);
+        if (val == 0) {
+            empty = true;
+        }
+        actual_shape_vec.push_back(val);
+        final_padded_shape_vec.push_back(std::max(output_dim_i(i, padded_ends), static_cast<uint32_t>(1)));
+    }
+    ttnn::Shape actual_shape(actual_shape_vec);
+    ttnn::Shape final_padded_shape(final_padded_shape_vec);
+
+    if (empty) {
+        TT_FATAL(
+            input_tensor.storage_type() == StorageType::DEVICE,
+            "Host tensor slice cannot return a scalar or empty tensor");
+        return ttnn::empty(
+            actual_shape, input_tensor.dtype(), input_tensor.layout(), input_tensor.mesh_device(), memory_config);
+    }
+
+    auto res =
+        tt::tt_metal::operation::run(
+            PaddedSliceDeviceOperation{
+                ttnn::Shape(modified_begins), ttnn::Shape(padded_ends), ttnn::Shape(modified_step), memory_config},
+            {input_tensor},
+            {},
+            {optional_output_tensor},
+            queue_id)
+            .at(0);
+
+    // If padded_slice should return a sharded tensor, then the op must created the sharded tensor in the requested
+    // memory config
+    if (res.is_sharded() && memory_config.is_sharded()) {
+        TT_ASSERT(
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.cpp
user: login: sminakov-tt
body: Should we make this TT_FATAL?
created_at: 2025-05-22T05:02:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,410 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sminakov-tt
body: Isn't this check redundant considering the OP requires the rank to be 4?
created_at: 2025-05-22T05:07:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,410 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    tt::log_info("Input Buffer : {}", input_tensor.get_storage());
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sminakov-tt
body: Please remove log_info
created_at: 2025-05-22T05:07:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,410 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    tt::log_info("Input Buffer : {}", input_tensor.get_storage());
+
+    auto num_cores_total = cores.size();
+
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+    bool is_block_sharded = output_tensor.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+    uint32_t num_cores_channels = 1;
+    auto total_cores = output_shard_spec.grid;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+    uint32_t input_page_size = input_shape[-1] * input_tensor.element_size();
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sminakov-tt
body: Please query page size from tensor, don't recalculate
created_at: 2025-05-22T05:08:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,410 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    tt::log_info("Input Buffer : {}", input_tensor.get_storage());
+
+    auto num_cores_total = cores.size();
+
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+    bool is_block_sharded = output_tensor.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+    uint32_t num_cores_channels = 1;
+    auto total_cores = output_shard_spec.grid;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+    uint32_t input_page_size = input_shape[-1] * input_tensor.element_size();
+    uint32_t input_row_size_bytes = input_shape[-1] * input_tensor.element_size() / num_cores_channels;
+
+    uint32_t output_row_size_bytes = output_shard_shape[1] * input_tensor.element_size();
+    uint32_t output_row_size_elems = output_shard_shape[1];
+
+    tt::log_debug("input_row_size_bytes: {}, output_row_size_bytes: {}", input_row_size_bytes, output_row_size_bytes);
+    std::uint32_t num_dims = static_cast<std::uint32_t>(input_shape.rank());
+    std::vector<uint32_t> num_output_sticks_per_dim(num_dims);
+    std::vector<uint32_t> num_input_sticks_per_dim(num_dims);
+    std::vector<uint32_t> id_per_dim(num_dims);
+
+    std::vector<uint32_t> accumulated_total_per_dim(num_dims);
+
+    // TODO: Remove first element of these arrays and update kernel accordingly
+    // This currently just matches tile version where we iterate over the row as well
+    num_output_sticks_per_dim[0] = 1;
+    num_input_sticks_per_dim[0] = 0;
+    accumulated_total_per_dim[0] = 1;
+
+    tt::log_debug("Output Shape : {}, Input Shape : {}", actual_output_shape, input_shape);
+    for (int32_t i = 1; i < num_dims; i++) {
+        uint32_t num_output_dim = actual_output_shape[-(i + 1)];
+        uint32_t num_total_dim = input_shape[-(i + 1)];
+        uint32_t num_input_dim = (num_total_dim - num_output_dim) * accumulated_total_per_dim[i - 1];
+        num_output_sticks_per_dim[i] = num_output_dim;
+        num_input_sticks_per_dim[i] = num_input_dim;
+        accumulated_total_per_dim[i] = num_total_dim * accumulated_total_per_dim[i - 1];
+    }
+
+    for (int i = 0; i < num_dims; i++) {
+        tt::log_debug(
+            "i = {}, num_output_sticks_per_dim: {}, num_input_sticks_per_dim: {}, accumulated_total_per_dim: {}",
+            i,
+            num_output_sticks_per_dim[i],
+            num_input_sticks_per_dim[i],
+            accumulated_total_per_dim[i]);
+    }
+    using namespace tt::tt_metal::experimental;
+    auto SRC_BUFFER_ALIGNMENT = input_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto DST_BUFFER_ALIGNMENT = output_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto ALIGNMENT = std::max(SRC_BUFFER_ALIGNMENT, DST_BUFFER_ALIGNMENT);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sminakov-tt
body: Please don't name variables in CAPS
created_at: 2025-05-22T05:09:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,410 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    tt::log_info("Input Buffer : {}", input_tensor.get_storage());
+
+    auto num_cores_total = cores.size();
+
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+    bool is_block_sharded = output_tensor.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+    uint32_t num_cores_channels = 1;
+    auto total_cores = output_shard_spec.grid;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+    uint32_t input_page_size = input_shape[-1] * input_tensor.element_size();
+    uint32_t input_row_size_bytes = input_shape[-1] * input_tensor.element_size() / num_cores_channels;
+
+    uint32_t output_row_size_bytes = output_shard_shape[1] * input_tensor.element_size();
+    uint32_t output_row_size_elems = output_shard_shape[1];
+
+    tt::log_debug("input_row_size_bytes: {}, output_row_size_bytes: {}", input_row_size_bytes, output_row_size_bytes);
+    std::uint32_t num_dims = static_cast<std::uint32_t>(input_shape.rank());
+    std::vector<uint32_t> num_output_sticks_per_dim(num_dims);
+    std::vector<uint32_t> num_input_sticks_per_dim(num_dims);
+    std::vector<uint32_t> id_per_dim(num_dims);
+
+    std::vector<uint32_t> accumulated_total_per_dim(num_dims);
+
+    // TODO: Remove first element of these arrays and update kernel accordingly
+    // This currently just matches tile version where we iterate over the row as well
+    num_output_sticks_per_dim[0] = 1;
+    num_input_sticks_per_dim[0] = 0;
+    accumulated_total_per_dim[0] = 1;
+
+    tt::log_debug("Output Shape : {}, Input Shape : {}", actual_output_shape, input_shape);
+    for (int32_t i = 1; i < num_dims; i++) {
+        uint32_t num_output_dim = actual_output_shape[-(i + 1)];
+        uint32_t num_total_dim = input_shape[-(i + 1)];
+        uint32_t num_input_dim = (num_total_dim - num_output_dim) * accumulated_total_per_dim[i - 1];
+        num_output_sticks_per_dim[i] = num_output_dim;
+        num_input_sticks_per_dim[i] = num_input_dim;
+        accumulated_total_per_dim[i] = num_total_dim * accumulated_total_per_dim[i - 1];
+    }
+
+    for (int i = 0; i < num_dims; i++) {
+        tt::log_debug(
+            "i = {}, num_output_sticks_per_dim: {}, num_input_sticks_per_dim: {}, accumulated_total_per_dim: {}",
+            i,
+            num_output_sticks_per_dim[i],
+            num_input_sticks_per_dim[i],
+            accumulated_total_per_dim[i]);
+    }
+    using namespace tt::tt_metal::experimental;
+    auto SRC_BUFFER_ALIGNMENT = input_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto DST_BUFFER_ALIGNMENT = output_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto ALIGNMENT = std::max(SRC_BUFFER_ALIGNMENT, DST_BUFFER_ALIGNMENT);
+    uint32_t begins_bytes = output_tensor_start[-1] * input_tensor.element_size();
+    uint32_t misalignment = begins_bytes % SRC_BUFFER_ALIGNMENT;
+
+    uint32_t output_row_size_bytes_offset = tt::round_up(output_row_size_bytes, ALIGNMENT);
+    uint32_t start_addr = input_tensor.buffer()->address();
+    std::vector<uint32_t> common_reader_kernel_args = {
+        start_addr + begins_bytes - misalignment,  // read from nearest aligned address
+        input_page_size,
+        input_row_size_bytes,
+        output_row_size_bytes_offset,
+        num_dims,
+        0,
+        0,
+        0,
+        0};
+
+    common_reader_kernel_args.insert(
+        common_reader_kernel_args.end(), num_output_sticks_per_dim.begin(), num_output_sticks_per_dim.end());
+    common_reader_kernel_args.insert(
+        common_reader_kernel_args.end(), num_input_sticks_per_dim.begin(), num_input_sticks_per_dim.end());
+
+    std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> ret_val(num_cores_total);
+
+    const auto num_sticks_per_core = output_shard_spec.shape[0];
+    uint32_t num_sticks_per_core_read = 0, num_read_per_barrier = 0;
+    if (num_sticks_per_core != 0) {
+        num_sticks_per_core_read =
+            tt::tt_metal::merge_num_sticks_to_read(num_sticks_per_core, output_row_size_bytes_offset, max_read_size);
+        num_read_per_barrier = num_sticks_per_core / num_sticks_per_core_read;
+    }
+    tt::log_debug(
+        "num_stick_per_core: {}, num_stick_per_core_read: {}, num_read_per_barrier: {}",
+        num_sticks_per_core,
+        num_sticks_per_core_read,
+        num_read_per_barrier);
+
+    uint32_t start_offset = get_rm_start_offset(input_tensor, output_tensor_start);
+    uint32_t core_index = 0;
+    for (const auto& core : cores) {
+        uint32_t core_w_index = 0;
+        uint32_t core_h_index = core_index;
+        if (is_block_sharded) {
+            core_w_index = rm_orientation ? core.x : core.y;
+            core_h_index = rm_orientation ? core.y : core.x;
+        }
+
+        const uint32_t num_sticks_written = core_h_index * num_sticks_per_core;
+        const uint32_t width_offset = core_w_index * output_row_size_bytes_offset;
+
+        id_per_dim[0] = num_sticks_written % num_output_sticks_per_dim[0];
+        uint32_t output_written = num_sticks_written / num_output_sticks_per_dim[0];
+        uint32_t start_id = id_per_dim[0] + start_offset;
+        for (uint32_t j = 1; j < num_dims; j++) {
+            id_per_dim[j] = output_written % num_output_sticks_per_dim[j];
+            output_written = output_written / num_output_sticks_per_dim[j];
+            start_id += id_per_dim[j] * accumulated_total_per_dim[j - 1];
+        }
+
+        std::vector<uint32_t> reader_kernel_args = common_reader_kernel_args;
+        reader_kernel_args[0] += width_offset;
+
+        uint32_t addr_offset = 5;
+        reader_kernel_args[addr_offset++] = start_id;
+        reader_kernel_args[addr_offset++] = num_sticks_per_core;
+        reader_kernel_args[addr_offset++] = num_sticks_per_core;
+        reader_kernel_args[addr_offset] = num_sticks_per_core;
+        reader_kernel_args.insert(reader_kernel_args.end(), id_per_dim.begin(), id_per_dim.end());
+
+        std::vector<uint32_t> writer_kernel_args = {
+            num_sticks_per_core, output_row_size_elems, input_row_size_bytes, output_row_size_bytes};
+        ret_val[core_index] = {reader_kernel_args, writer_kernel_args};
+        core_index++;
+    }
+
+    return ret_val;
+}
+
+operation::ProgramWithCallbacks padded_slice_rm_multi_core(
+    const Tensor& a, Tensor& output, const ttnn::Shape& output_tensor_start, const ttnn::Shape& output_tensor_end) {
+    const ttnn::Shape output_shape = output.get_logical_shape();
+    ttnn::Shape actual_output_shape = output_tensor_end;
+    for (int i = 0; i < output_shape.rank(); i++) {
+        actual_output_shape[i] = output_tensor_end[i] - output_tensor_start[i];
+    }
+
+    tt::tt_metal::Program program = tt::tt_metal::CreateProgram();
+
+    // This should allocate a DRAM buffer on the device
+    tt::tt_metal::IDevice* device = a.device();
+
+    uint32_t num_unpadded_sticks = output.volume() / output.get_padded_shape()[-1];
+
+    auto compute_with_storage_grid_size = device->compute_with_storage_grid_size();
+    uint32_t num_cores_x = compute_with_storage_grid_size.x;
+    uint32_t num_cores_y = compute_with_storage_grid_size.y;
+
+    tt::tt_metal::Buffer* src0_buffer = a.buffer();
+
+    tt::DataFormat cb_data_format = tt::tt_metal::datatype_to_dataformat_converter(a.get_dtype());
+
+    bool is_output_sharded = output.is_sharded();
+
+    TT_FATAL(is_output_sharded, "Output Tensor must be sharded.");
+    auto output_shard_spec = output.shard_spec().value();
+
+    bool is_block_sharded = output.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+
+    uint32_t output_row_size_bytes = output_shard_spec.shape[1] * output.element_size();
+
+    CoreRangeSet total_cores = output.shard_spec().value().grid;
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+
+    std::vector<CoreCoord> iter_cores = corerange_to_cores(total_cores, std::nullopt, rm_orientation);
+    uint32_t num_cores_total = total_cores.num_cores();
+
+    uint32_t num_cores_channels = 1;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+
+    bool pad_output_row = false;
+
+    TT_FATAL(
+        a.get_logical_shape()[3] % num_cores_channels == 0,
+        "Input tensor should be divisible by number of cores in channel dimension");
+    uint32_t input_row_size_bytes = a.get_logical_shape()[-1] * a.element_size();
+    input_row_size_bytes = input_row_size_bytes / num_cores_channels;
+
+    tt::tt_metal::Buffer* dst_buffer = output.buffer();
+    TT_ASSERT(dst_buffer != nullptr, "Output buffer should be allocated on device!");
+    // TT_FATAL(!output.is_sharded(),"Output tensor should not be sharded!");
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sminakov-tt
body: Please remove the comment
created_at: 2025-05-22T05:10:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -47,11 +47,12 @@
 #include "ttnn/operations/experimental/unary_backward/gelu_backward/gelu_backward_pybind.hpp"
 #include "ttnn/operations/experimental/reduction/sort/sort_pybind.hpp"
 #include "ttnn/operations/experimental/gather/gather_pybind.hpp"
-
+#include "ttnn/operations/experimental/padded_slice/padded_slice_pybind.hpp"
 namespace ttnn::operations::experimental {
path: ttnn/cpp/ttnn/operations/experimental/experimental_pybind.cpp
user: login: sankarmanoj-tt
body: Fixed. 
created_at: 2025-05-22T05:13:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include <tt-metalium/constants.hpp>
+#include "padded_slice_op.hpp"
+#include "padded_slice_program_factory.hpp"
+
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental {
+
+void PaddedSliceDeviceOperation::validate_with_output_tensors(
+    const std::vector<Tensor>& input_tensors, const std::vector<std::optional<Tensor>>& output_tensors) const {
+    using namespace tt::constants;
+    const bool has_step = std::any_of(this->step.cbegin(), this->step.cend(), [](uint32_t s) { return s != 1; });
+    const auto& input_tensor_a = input_tensors.at(0);
+    TT_FATAL(input_tensor_a.storage_type() == StorageType::DEVICE, "Operands to unpad need to be on device!");
+    TT_FATAL(input_tensor_a.buffer() != nullptr, "Operands to unpad need to be allocated in buffers on device!");
+    TT_FATAL(input_tensor_a.get_layout() == Layout::ROW_MAJOR, "Input to padded_slice must be in row major layout");
+    TT_FATAL(input_tensor_a.get_padded_shape().rank() == 4, "Only 4D tensors are supported for padded_slice");
+    TT_FATAL(
+        input_tensor_a.get_padded_shape().rank() == this->padded_slice_start.rank() &&
+            this->padded_slice_start.rank() == this->padded_slice_end.rank(),
+        "Padded slice start, end and input tensor must all have the same rank");
+    for (uint32_t i = 0; i < input_tensor_a.get_padded_shape().rank(); i++) {
+        TT_FATAL(
+            this->padded_slice_start[i] < input_tensor_a.get_padded_shape()[i],
+            "Starts {} must be less than the shape of the tensor {} at index {}",
+            this->padded_slice_start[i],
+            input_tensor_a.get_padded_shape()[i],
+            i);
+        TT_FATAL(
+            this->padded_slice_end[i] <= input_tensor_a.get_padded_shape()[i],
+            "Ends {} must be less than or equal to the shape of the tensor {}",
+            this->padded_slice_end[i],
+            input_tensor_a.get_padded_shape()[i]);
+        // Check if start shape is <= end shape
+        TT_FATAL(
+            this->padded_slice_start[i] <= this->padded_slice_end[i],
+            "Slice start {} must be less than or equal to the end {}",
+            this->padded_slice_start[i],
+            this->padded_slice_end[i]);
+    }
+    if (!output_tensors.empty() && output_tensors[0].has_value()) {
+        const auto output_shape_required = compute_output_specs(input_tensors)[0].logical_shape();
+        const auto& out_tensor = output_tensors[0].value();
+        TT_FATAL(
+            out_tensor.get_padded_shape() == output_shape_required,
+            "The input tensors need a shape of {}, however the output tensor is only {}",
+            output_shape_required,
+            out_tensor.get_padded_shape());
+    }
+    auto output_tensor_shape = this->compute_output_specs(input_tensors)[0].logical_shape();
+    TT_FATAL(!has_step, "Padded slice does not support strided slices");
+}
+
+std::vector<ttnn::TensorSpec> PaddedSliceDeviceOperation::compute_output_specs(
+    const std::vector<Tensor>& input_tensors) const {
+    const auto& input_tensor = input_tensors[0];
+    SmallVector<uint32_t> out_shape(input_tensor.get_logical_shape().rank());
+
+    auto output_dim_i = [this](size_t i) {
+        return (this->padded_slice_end[i] - this->padded_slice_start[i] + this->step[i] - 1) / this->step[i];
+    };
+    for (uint32_t i = 0; i < out_shape.size(); i++) {
+        out_shape[i] = output_dim_i(i);
+    }
+    out_shape[2] = out_shape[0] * out_shape[1] * out_shape[2];
+    out_shape[0] = 1;
+    out_shape[1] = 1;
+
+    if (this->output_mem_config.memory_layout() == TensorMemoryLayout::HEIGHT_SHARDED) {
+        auto output_shard_shape = this->output_mem_config.shard_spec().value().shape;
+        out_shape[out_shape.size() - 1] = output_shard_shape[1];
+    }
+
+    ttnn::Shape output_tensor_shape(std::move(out_shape));
+    auto tensor_layout = TensorLayout::fromPaddedShape(
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_op.cpp
user: login: sankarmanoj-tt
body: Fixed. 
created_at: 2025-05-22T05:13:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,218 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "device/padded_slice_op.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/run_operation.hpp"
+#include "ttnn/common/constants.hpp"
+#include "ttnn/common/queue_id.hpp"
+#include "ttnn/operations/creation.hpp"
+#include "ttnn/operations/core/core.hpp"
+#include "ttnn/operations/data_movement/common/common.hpp"
+#include "ttnn/operations/data_movement/fill_pad/fill_pad.hpp"
+#include "ttnn/operations/experimental/reshape/view.hpp"
+#include "ttnn/tensor/types.hpp"
+#include "padded_slice.hpp"
+
+namespace ttnn::operations::experimental {
+
+template <typename T>
+ttnn::Tensor PaddedSliceOperation::invoke(
+    QueueId queue_id,
+    const ttnn::Tensor& input_tensor,
+    tt::stl::Span<const T> begins,
+    tt::stl::Span<const T> ends,
+    tt::stl::Span<const T> step,
+    const MemoryConfig& memory_config,
+    const std::optional<Tensor>& optional_output_tensor,
+    const std::optional<float>& pad_value) {
+    // Ensure start and end vectors have matching sizes and correct tensor rank
+
+    const auto& input_shape = input_tensor.get_logical_shape();
+    uint32_t input_rank = input_shape.rank();
+    auto input_layout = input_tensor.get_layout();
+
+    TT_FATAL(input_rank == 4, "Only 4D tensors are supported for padded_slice");
+
+    if (input_rank == 0) {
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.cpp
user: login: sankarmanoj-tt
body: Removed this check. 
created_at: 2025-05-22T05:13:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,410 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    tt::log_info("Input Buffer : {}", input_tensor.get_storage());
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sankarmanoj-tt
body: Removed
created_at: 2025-05-22T05:13:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "ttnn/decorators.hpp"
+
+namespace ttnn {
+namespace operations {
+namespace experimental {
+
+struct PaddedSliceOperation {
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        tt::stl::Span<const T> begins,
+        tt::stl::Span<const T> ends,
+        tt::stl::Span<const T> step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const ttnn::SmallVector<T>& begins,
+        const ttnn::SmallVector<T>& ends,
+        const ttnn::SmallVector<T>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt) {
+        return invoke(
+            queue_id,
+            input_tensor,
+            tt::stl::Span<const T>(begins),
+            tt::stl::Span<const T>(ends),
+            tt::stl::Span<const T>(step),
+            memory_config_arg,
+            optional_output_tensor,
+            pad_value);
+    }
+
+    template <typename T, std::size_t N>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const std::array<T, N>& output_tensor_start,
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.hpp
user: login: sminakov-tt
body: Please remove array<T, N> overload, it's redundant
created_at: 2025-05-22T05:17:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,218 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "device/padded_slice_op.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/run_operation.hpp"
+#include "ttnn/common/constants.hpp"
+#include "ttnn/common/queue_id.hpp"
+#include "ttnn/operations/creation.hpp"
+#include "ttnn/operations/core/core.hpp"
+#include "ttnn/operations/data_movement/common/common.hpp"
+#include "ttnn/operations/data_movement/fill_pad/fill_pad.hpp"
+#include "ttnn/operations/experimental/reshape/view.hpp"
+#include "ttnn/tensor/types.hpp"
+#include "padded_slice.hpp"
+
+namespace ttnn::operations::experimental {
+
+template <typename T>
+ttnn::Tensor PaddedSliceOperation::invoke(
+    QueueId queue_id,
+    const ttnn::Tensor& input_tensor,
+    tt::stl::Span<const T> begins,
+    tt::stl::Span<const T> ends,
+    tt::stl::Span<const T> step,
+    const MemoryConfig& memory_config,
+    const std::optional<Tensor>& optional_output_tensor,
+    const std::optional<float>& pad_value) {
+    // Ensure start and end vectors have matching sizes and correct tensor rank
+
+    const auto& input_shape = input_tensor.get_logical_shape();
+    uint32_t input_rank = input_shape.rank();
+    auto input_layout = input_tensor.get_layout();
+
+    TT_FATAL(input_rank == 4, "Only 4D tensors are supported for padded_slice");
+
+    if (input_rank == 0) {
+        return input_tensor;
+    }
+    TT_FATAL(
+        input_rank == begins.size(), "Input rank {} and begins {} must have the same size", input_rank, begins.size());
+    TT_FATAL(begins.size() == ends.size(), "Start {} and end {} must have the same size", begins.size(), ends.size());
+    TT_FATAL(
+        step.size() == begins.size(),
+        "Step {} must have the same size as start {} and end",
+        step.size(),
+        begins.size());
+
+    bool no_step = std::ranges::all_of(step, [](uint32_t s) { return s == 1; });
+    bool starts_zero = std::ranges::all_of(begins, [](uint32_t s) { return s == 0; });
+    bool ends_max = true;
+    for (size_t i = 0; i < ends.size(); ++i) {
+        ends_max &= ends[i] == input_shape[i];
+        if (!ends_max) {
+            break;
+        }
+    }
+
+    TT_FATAL(no_step, "Steps != 1 are not supported for padded_slice.");
+    TT_FATAL(input_layout == Layout::ROW_MAJOR, "Only Row Major Inputs are supported for padded_slice.");
+    TT_FATAL(memory_config.is_sharded(), "Output Memory Config must be sharded. Use slice for non-sharded outputs.");
+    TT_FATAL(!input_tensor.memory_config().is_sharded(), " padded_slice does not support sharded inputs.");
+
+    const auto& tile_shape = input_tensor.get_tensor_spec().tile().get_tile_shape();
+
+    auto ret_adjustment([&](const ttnn::Tensor& ret_input_tensor) {
+        if (ret_input_tensor.storage_type() == StorageType::DEVICE) {
+            auto tensor = ttnn::to_memory_config(ret_input_tensor, memory_config, std::nullopt);
+            tensor = ttnn::to_layout(tensor, input_layout, std::nullopt, std::nullopt, (IDevice*)nullptr);
+            return tensor;
+        }
+        return ret_input_tensor;
+    });
+
+    // No-op check
+    if (no_step && starts_zero && ends_max) {
+        return ret_adjustment(input_tensor);
+    }
+
+    // Create modified vectors with wrapped indices and adjust them to match the tensor's rank
+    ttnn::SmallVector<uint32_t> modified_begins(input_rank, 0);
+    ttnn::SmallVector<uint32_t> modified_ends(input_rank, 0);
+    ttnn::SmallVector<uint32_t> modified_step(input_rank, 1);
+
+    // Wrap indices and adjust begins, ends, and step
+    for (size_t i = 0; i < begins.size(); ++i) {
+        if constexpr (std::is_signed_v<T>) {
+            modified_begins[i] = data_movement::wrap_index(begins[i], input_shape[i]);
+            modified_ends[i] = data_movement::wrap_index(ends[i], input_shape[i]);
+            modified_step[i] = static_cast<uint32_t>(step[i]);
+        } else {
+            modified_begins[i] = begins[i];
+            modified_ends[i] = ends[i];
+            modified_step[i] = step[i];
+        }
+    }
+
+    auto output_dim_i = [&modified_begins, &modified_step](size_t i, const ttnn::SmallVector<uint32_t>& modified_ends) {
+        return (modified_ends[i] - modified_begins[i] + modified_step[i] - 1) / modified_step[i];
+    };
+
+    auto check_handled_tile_alignment = [&modified_begins, &input_rank, &tile_shape]() -> bool {
+        return (
+            modified_begins[input_rank - 1] % tile_shape[1] == 0 &&
+            modified_begins[input_rank - 2] % tile_shape[0] == 0);
+    };
+
+    bool rm_only = false;
+    bool one_dimensional = input_rank == 1;
+    bool handled_tile_alignment = one_dimensional ? true : check_handled_tile_alignment();
+
+    ttnn::SmallVector<uint32_t> padded_ends = modified_ends;
+
+    ttnn::SmallVector<uint32_t> actual_shape_vec, final_padded_shape_vec;
+    actual_shape_vec.reserve(input_rank);
+    final_padded_shape_vec.reserve(input_rank);
+    bool empty = false;
+
+    // Compute actual and padded shapes for the original input rank
+    for (size_t i = 0; i < input_rank; ++i) {
+        TT_FATAL(
+            modified_ends[i] >= modified_begins[i],
+            "End {} must be greater than or equal to start {}",
+            modified_ends[i],
+            modified_begins[i]);
+        auto val = output_dim_i(i, modified_ends);
+        if (val == 0) {
+            empty = true;
+        }
+        actual_shape_vec.push_back(val);
+        final_padded_shape_vec.push_back(std::max(output_dim_i(i, padded_ends), static_cast<uint32_t>(1)));
+    }
+    ttnn::Shape actual_shape(actual_shape_vec);
+    ttnn::Shape final_padded_shape(final_padded_shape_vec);
+
+    if (empty) {
+        TT_FATAL(
+            input_tensor.storage_type() == StorageType::DEVICE,
+            "Host tensor slice cannot return a scalar or empty tensor");
+        return ttnn::empty(
+            actual_shape, input_tensor.dtype(), input_tensor.layout(), input_tensor.mesh_device(), memory_config);
+    }
+
+    auto res =
+        tt::tt_metal::operation::run(
+            PaddedSliceDeviceOperation{
+                ttnn::Shape(modified_begins), ttnn::Shape(padded_ends), ttnn::Shape(modified_step), memory_config},
+            {input_tensor},
+            {},
+            {optional_output_tensor},
+            queue_id)
+            .at(0);
+
+    // If padded_slice should return a sharded tensor, then the op must created the sharded tensor in the requested
+    // memory config
+    if (res.is_sharded() && memory_config.is_sharded()) {
+        TT_ASSERT(
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.cpp
user: login: sankarmanoj-tt
body: I think it is okay to leave this TT_ASSERT, as this is more of a sanity check of the output. 
created_at: 2025-05-22T05:17:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "ttnn/decorators.hpp"
+
+namespace ttnn {
+namespace operations {
+namespace experimental {
+
+struct PaddedSliceOperation {
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        tt::stl::Span<const T> begins,
+        tt::stl::Span<const T> ends,
+        tt::stl::Span<const T> step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const ttnn::SmallVector<T>& begins,
+        const ttnn::SmallVector<T>& ends,
+        const ttnn::SmallVector<T>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt) {
+        return invoke(
+            queue_id,
+            input_tensor,
+            tt::stl::Span<const T>(begins),
+            tt::stl::Span<const T>(ends),
+            tt::stl::Span<const T>(step),
+            memory_config_arg,
+            optional_output_tensor,
+            pad_value);
+    }
+
+    template <typename T, std::size_t N>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const std::array<T, N>& output_tensor_start,
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.hpp
user: login: sankarmanoj-tt
body: I'm using this in pybind with std::array<uint32_t, 4>
created_at: 2025-05-22T05:18:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "ttnn/decorators.hpp"
+
+namespace ttnn {
+namespace operations {
+namespace experimental {
+
+struct PaddedSliceOperation {
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        tt::stl::Span<const T> begins,
+        tt::stl::Span<const T> ends,
+        tt::stl::Span<const T> step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const ttnn::SmallVector<T>& begins,
+        const ttnn::SmallVector<T>& ends,
+        const ttnn::SmallVector<T>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt) {
+        return invoke(
+            queue_id,
+            input_tensor,
+            tt::stl::Span<const T>(begins),
+            tt::stl::Span<const T>(ends),
+            tt::stl::Span<const T>(step),
+            memory_config_arg,
+            optional_output_tensor,
+            pad_value);
+    }
+
+    template <typename T, std::size_t N>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const std::array<T, N>& output_tensor_start,
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.hpp
user: login: sminakov-tt
body: Yes, please remove it from the pybind too. Binding the version with SmallVector should be enough
created_at: 2025-05-22T05:25:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "ttnn/decorators.hpp"
+
+namespace ttnn {
+namespace operations {
+namespace experimental {
+
+struct PaddedSliceOperation {
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        tt::stl::Span<const T> begins,
+        tt::stl::Span<const T> ends,
+        tt::stl::Span<const T> step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt);
+
+    template <typename T>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const ttnn::SmallVector<T>& begins,
+        const ttnn::SmallVector<T>& ends,
+        const ttnn::SmallVector<T>& step,
+        const MemoryConfig& memory_config_arg,
+        const std::optional<Tensor>& optional_output_tensor = std::nullopt,
+        const std::optional<float>& pad_value = std::nullopt) {
+        return invoke(
+            queue_id,
+            input_tensor,
+            tt::stl::Span<const T>(begins),
+            tt::stl::Span<const T>(ends),
+            tt::stl::Span<const T>(step),
+            memory_config_arg,
+            optional_output_tensor,
+            pad_value);
+    }
+
+    template <typename T, std::size_t N>
+    static ttnn::Tensor invoke(
+        QueueId queue_id,
+        const ttnn::Tensor& input_tensor,
+        const std::array<T, N>& output_tensor_start,
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/padded_slice.hpp
user: login: sankarmanoj-tt
body: Removed. 
created_at: 2025-05-22T08:38:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,410 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    tt::log_info("Input Buffer : {}", input_tensor.get_storage());
+
+    auto num_cores_total = cores.size();
+
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+    bool is_block_sharded = output_tensor.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+    uint32_t num_cores_channels = 1;
+    auto total_cores = output_shard_spec.grid;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+    uint32_t input_page_size = input_shape[-1] * input_tensor.element_size();
+    uint32_t input_row_size_bytes = input_shape[-1] * input_tensor.element_size() / num_cores_channels;
+
+    uint32_t output_row_size_bytes = output_shard_shape[1] * input_tensor.element_size();
+    uint32_t output_row_size_elems = output_shard_shape[1];
+
+    tt::log_debug("input_row_size_bytes: {}, output_row_size_bytes: {}", input_row_size_bytes, output_row_size_bytes);
+    std::uint32_t num_dims = static_cast<std::uint32_t>(input_shape.rank());
+    std::vector<uint32_t> num_output_sticks_per_dim(num_dims);
+    std::vector<uint32_t> num_input_sticks_per_dim(num_dims);
+    std::vector<uint32_t> id_per_dim(num_dims);
+
+    std::vector<uint32_t> accumulated_total_per_dim(num_dims);
+
+    // TODO: Remove first element of these arrays and update kernel accordingly
+    // This currently just matches tile version where we iterate over the row as well
+    num_output_sticks_per_dim[0] = 1;
+    num_input_sticks_per_dim[0] = 0;
+    accumulated_total_per_dim[0] = 1;
+
+    tt::log_debug("Output Shape : {}, Input Shape : {}", actual_output_shape, input_shape);
+    for (int32_t i = 1; i < num_dims; i++) {
+        uint32_t num_output_dim = actual_output_shape[-(i + 1)];
+        uint32_t num_total_dim = input_shape[-(i + 1)];
+        uint32_t num_input_dim = (num_total_dim - num_output_dim) * accumulated_total_per_dim[i - 1];
+        num_output_sticks_per_dim[i] = num_output_dim;
+        num_input_sticks_per_dim[i] = num_input_dim;
+        accumulated_total_per_dim[i] = num_total_dim * accumulated_total_per_dim[i - 1];
+    }
+
+    for (int i = 0; i < num_dims; i++) {
+        tt::log_debug(
+            "i = {}, num_output_sticks_per_dim: {}, num_input_sticks_per_dim: {}, accumulated_total_per_dim: {}",
+            i,
+            num_output_sticks_per_dim[i],
+            num_input_sticks_per_dim[i],
+            accumulated_total_per_dim[i]);
+    }
+    using namespace tt::tt_metal::experimental;
+    auto SRC_BUFFER_ALIGNMENT = input_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto DST_BUFFER_ALIGNMENT = output_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto ALIGNMENT = std::max(SRC_BUFFER_ALIGNMENT, DST_BUFFER_ALIGNMENT);
+    uint32_t begins_bytes = output_tensor_start[-1] * input_tensor.element_size();
+    uint32_t misalignment = begins_bytes % SRC_BUFFER_ALIGNMENT;
+
+    uint32_t output_row_size_bytes_offset = tt::round_up(output_row_size_bytes, ALIGNMENT);
+    uint32_t start_addr = input_tensor.buffer()->address();
+    std::vector<uint32_t> common_reader_kernel_args = {
+        start_addr + begins_bytes - misalignment,  // read from nearest aligned address
+        input_page_size,
+        input_row_size_bytes,
+        output_row_size_bytes_offset,
+        num_dims,
+        0,
+        0,
+        0,
+        0};
+
+    common_reader_kernel_args.insert(
+        common_reader_kernel_args.end(), num_output_sticks_per_dim.begin(), num_output_sticks_per_dim.end());
+    common_reader_kernel_args.insert(
+        common_reader_kernel_args.end(), num_input_sticks_per_dim.begin(), num_input_sticks_per_dim.end());
+
+    std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> ret_val(num_cores_total);
+
+    const auto num_sticks_per_core = output_shard_spec.shape[0];
+    uint32_t num_sticks_per_core_read = 0, num_read_per_barrier = 0;
+    if (num_sticks_per_core != 0) {
+        num_sticks_per_core_read =
+            tt::tt_metal::merge_num_sticks_to_read(num_sticks_per_core, output_row_size_bytes_offset, max_read_size);
+        num_read_per_barrier = num_sticks_per_core / num_sticks_per_core_read;
+    }
+    tt::log_debug(
+        "num_stick_per_core: {}, num_stick_per_core_read: {}, num_read_per_barrier: {}",
+        num_sticks_per_core,
+        num_sticks_per_core_read,
+        num_read_per_barrier);
+
+    uint32_t start_offset = get_rm_start_offset(input_tensor, output_tensor_start);
+    uint32_t core_index = 0;
+    for (const auto& core : cores) {
+        uint32_t core_w_index = 0;
+        uint32_t core_h_index = core_index;
+        if (is_block_sharded) {
+            core_w_index = rm_orientation ? core.x : core.y;
+            core_h_index = rm_orientation ? core.y : core.x;
+        }
+
+        const uint32_t num_sticks_written = core_h_index * num_sticks_per_core;
+        const uint32_t width_offset = core_w_index * output_row_size_bytes_offset;
+
+        id_per_dim[0] = num_sticks_written % num_output_sticks_per_dim[0];
+        uint32_t output_written = num_sticks_written / num_output_sticks_per_dim[0];
+        uint32_t start_id = id_per_dim[0] + start_offset;
+        for (uint32_t j = 1; j < num_dims; j++) {
+            id_per_dim[j] = output_written % num_output_sticks_per_dim[j];
+            output_written = output_written / num_output_sticks_per_dim[j];
+            start_id += id_per_dim[j] * accumulated_total_per_dim[j - 1];
+        }
+
+        std::vector<uint32_t> reader_kernel_args = common_reader_kernel_args;
+        reader_kernel_args[0] += width_offset;
+
+        uint32_t addr_offset = 5;
+        reader_kernel_args[addr_offset++] = start_id;
+        reader_kernel_args[addr_offset++] = num_sticks_per_core;
+        reader_kernel_args[addr_offset++] = num_sticks_per_core;
+        reader_kernel_args[addr_offset] = num_sticks_per_core;
+        reader_kernel_args.insert(reader_kernel_args.end(), id_per_dim.begin(), id_per_dim.end());
+
+        std::vector<uint32_t> writer_kernel_args = {
+            num_sticks_per_core, output_row_size_elems, input_row_size_bytes, output_row_size_bytes};
+        ret_val[core_index] = {reader_kernel_args, writer_kernel_args};
+        core_index++;
+    }
+
+    return ret_val;
+}
+
+operation::ProgramWithCallbacks padded_slice_rm_multi_core(
+    const Tensor& a, Tensor& output, const ttnn::Shape& output_tensor_start, const ttnn::Shape& output_tensor_end) {
+    const ttnn::Shape output_shape = output.get_logical_shape();
+    ttnn::Shape actual_output_shape = output_tensor_end;
+    for (int i = 0; i < output_shape.rank(); i++) {
+        actual_output_shape[i] = output_tensor_end[i] - output_tensor_start[i];
+    }
+
+    tt::tt_metal::Program program = tt::tt_metal::CreateProgram();
+
+    // This should allocate a DRAM buffer on the device
+    tt::tt_metal::IDevice* device = a.device();
+
+    uint32_t num_unpadded_sticks = output.volume() / output.get_padded_shape()[-1];
+
+    auto compute_with_storage_grid_size = device->compute_with_storage_grid_size();
+    uint32_t num_cores_x = compute_with_storage_grid_size.x;
+    uint32_t num_cores_y = compute_with_storage_grid_size.y;
+
+    tt::tt_metal::Buffer* src0_buffer = a.buffer();
+
+    tt::DataFormat cb_data_format = tt::tt_metal::datatype_to_dataformat_converter(a.get_dtype());
+
+    bool is_output_sharded = output.is_sharded();
+
+    TT_FATAL(is_output_sharded, "Output Tensor must be sharded.");
+    auto output_shard_spec = output.shard_spec().value();
+
+    bool is_block_sharded = output.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+
+    uint32_t output_row_size_bytes = output_shard_spec.shape[1] * output.element_size();
+
+    CoreRangeSet total_cores = output.shard_spec().value().grid;
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+
+    std::vector<CoreCoord> iter_cores = corerange_to_cores(total_cores, std::nullopt, rm_orientation);
+    uint32_t num_cores_total = total_cores.num_cores();
+
+    uint32_t num_cores_channels = 1;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+
+    bool pad_output_row = false;
+
+    TT_FATAL(
+        a.get_logical_shape()[3] % num_cores_channels == 0,
+        "Input tensor should be divisible by number of cores in channel dimension");
+    uint32_t input_row_size_bytes = a.get_logical_shape()[-1] * a.element_size();
+    input_row_size_bytes = input_row_size_bytes / num_cores_channels;
+
+    tt::tt_metal::Buffer* dst_buffer = output.buffer();
+    TT_ASSERT(dst_buffer != nullptr, "Output buffer should be allocated on device!");
+    // TT_FATAL(!output.is_sharded(),"Output tensor should not be sharded!");
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sankarmanoj-tt
body: Removed
created_at: 2025-05-22T08:38:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,410 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sankarmanoj-tt
body: This is a utility function. In case we decide to add support for other ranks, this check would be good to have.
created_at: 2025-05-22T08:55:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,410 @@
+// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#include "optional"
+#include "tt-metalium/assert.hpp"
+#include "tt-metalium/logger.hpp"
+#include "ttnn/operations/math.hpp"
+#include <tt-metalium/work_split.hpp>
+#include <tt-metalium/constants.hpp>
+#include <tt-metalium/util.hpp>
+#include <tt-metalium/hal.hpp>
+#include <tt-metalium/host_api.hpp>
+
+#include "padded_slice_op.hpp"
+using namespace tt::constants;
+using namespace tt::tt_metal;
+
+namespace ttnn::operations::experimental::detail {
+
+uint32_t get_upper_dims_compressed(const ttnn::Shape& shape) {
+    return std::accumulate(shape.cbegin(), shape.cend() - 2, 1, std::multiplies<uint32_t>{});
+}
+
+uint32_t get_upper_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    // offset for every dim except last 2
+    uint32_t start_offset = 0;
+    const auto& shape = tensor.get_padded_shape();
+
+    uint32_t num_pages = tensor.volume();
+    if (tensor.get_layout() == Layout::TILE) {
+        num_pages /= tt::constants::TILE_HW;
+    } else {
+        uint32_t page_width = shape[-1];
+        num_pages /= page_width;
+    }
+
+    for (uint32_t dim_outer = 0; dim_outer < shape.rank() - 2; dim_outer++) {
+        uint32_t compressed_dims = 1;
+        for (uint32_t dim_inner = 0; dim_inner <= dim_outer; dim_inner++) {
+            compressed_dims *= shape[dim_inner];
+        }
+        start_offset += (num_pages / compressed_dims) * padded_slice_start[dim_outer];
+    }
+    return start_offset;
+}
+
+static uint32_t get_rm_start_offset(const Tensor& tensor, const ttnn::Shape& padded_slice_start) {
+    uint32_t start_offset = 0;
+
+    if (tensor.get_padded_shape().rank() >= 2) {
+        const auto& shape = tensor.get_padded_shape();
+        uint32_t num_pages = tensor.volume() / shape[-1];
+        uint32_t upper_dims_compressed = get_upper_dims_compressed(shape);
+        start_offset = get_upper_start_offset(tensor, padded_slice_start);
+        start_offset += padded_slice_start[-2];
+    }
+
+    return start_offset;
+}
+
+std::vector<std::pair<std::vector<uint32_t>, std::vector<uint32_t>>> get_padded_slice_runtime_args_rm_sharded_output(
+    const Tensor& input_tensor,
+    Tensor& output_tensor,
+    const ttnn::Shape& output_tensor_start,
+    const ttnn::Shape& actual_output_shape,
+    const std::vector<CoreCoord>& cores,
+    uint32_t max_read_size) {
+    tt::tt_metal::IDevice* device = input_tensor.device();
+
+    auto input_buffer = input_tensor.buffer();
+    auto output_buffer = output_tensor.buffer();
+    auto input_shape = input_tensor.get_logical_shape();
+    auto output_shape = output_tensor.get_logical_shape();
+    auto output_shard_spec = output_tensor.shard_spec().value();
+    auto output_shard_shape = output_shard_spec.shape;
+
+    tt::log_info("Input Buffer : {}", input_tensor.get_storage());
+
+    auto num_cores_total = cores.size();
+
+    bool rm_orientation = output_shard_spec.orientation == ShardOrientation::ROW_MAJOR;
+    bool is_block_sharded = output_tensor.memory_config().memory_layout() == TensorMemoryLayout::BLOCK_SHARDED;
+    uint32_t num_cores_channels = 1;
+    auto total_cores = output_shard_spec.grid;
+    if (is_block_sharded) {
+        if (rm_orientation) {
+            num_cores_channels = total_cores.bounding_box().grid_size().x;
+        } else {
+            num_cores_channels = total_cores.bounding_box().grid_size().y;
+        }
+    }
+    uint32_t input_page_size = input_shape[-1] * input_tensor.element_size();
+    uint32_t input_row_size_bytes = input_shape[-1] * input_tensor.element_size() / num_cores_channels;
+
+    uint32_t output_row_size_bytes = output_shard_shape[1] * input_tensor.element_size();
+    uint32_t output_row_size_elems = output_shard_shape[1];
+
+    tt::log_debug("input_row_size_bytes: {}, output_row_size_bytes: {}", input_row_size_bytes, output_row_size_bytes);
+    std::uint32_t num_dims = static_cast<std::uint32_t>(input_shape.rank());
+    std::vector<uint32_t> num_output_sticks_per_dim(num_dims);
+    std::vector<uint32_t> num_input_sticks_per_dim(num_dims);
+    std::vector<uint32_t> id_per_dim(num_dims);
+
+    std::vector<uint32_t> accumulated_total_per_dim(num_dims);
+
+    // TODO: Remove first element of these arrays and update kernel accordingly
+    // This currently just matches tile version where we iterate over the row as well
+    num_output_sticks_per_dim[0] = 1;
+    num_input_sticks_per_dim[0] = 0;
+    accumulated_total_per_dim[0] = 1;
+
+    tt::log_debug("Output Shape : {}, Input Shape : {}", actual_output_shape, input_shape);
+    for (int32_t i = 1; i < num_dims; i++) {
+        uint32_t num_output_dim = actual_output_shape[-(i + 1)];
+        uint32_t num_total_dim = input_shape[-(i + 1)];
+        uint32_t num_input_dim = (num_total_dim - num_output_dim) * accumulated_total_per_dim[i - 1];
+        num_output_sticks_per_dim[i] = num_output_dim;
+        num_input_sticks_per_dim[i] = num_input_dim;
+        accumulated_total_per_dim[i] = num_total_dim * accumulated_total_per_dim[i - 1];
+    }
+
+    for (int i = 0; i < num_dims; i++) {
+        tt::log_debug(
+            "i = {}, num_output_sticks_per_dim: {}, num_input_sticks_per_dim: {}, accumulated_total_per_dim: {}",
+            i,
+            num_output_sticks_per_dim[i],
+            num_input_sticks_per_dim[i],
+            accumulated_total_per_dim[i]);
+    }
+    using namespace tt::tt_metal::experimental;
+    auto SRC_BUFFER_ALIGNMENT = input_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto DST_BUFFER_ALIGNMENT = output_tensor.buffer()->buffer_type() == tt::tt_metal::BufferType::DRAM
+                                    ? hal::get_dram_alignment()
+                                    : hal::get_l1_alignment();
+    auto ALIGNMENT = std::max(SRC_BUFFER_ALIGNMENT, DST_BUFFER_ALIGNMENT);
path: ttnn/cpp/ttnn/operations/experimental/padded_slice/device/padded_slice_program_factory.cpp
user: login: sankarmanoj-tt
body: Fixed.
created_at: 2025-05-22T09:00:55Z
author_association: CONTRIBUTOR

PR comments:
user: login: amorrisonTT
created_at: 2025-05-15T16:43:39Z
body: Why not build this into the `data_movement/slice`? 
author_association: CONTRIBUTOR
user: login: sankarmanoj-tt
created_at: 2025-05-16T19:39:16Z
body: @amorrisonTT Saad had suggested that this functionality be moved to a separate experimental op. 
author_association: CONTRIBUTOR
user: login: amorrisonTT
created_at: 2025-05-16T19:46:46Z
body: > @amorrisonTT Saad had suggested that this functionality be moved to a separate experimental op.

That's fair. Do minimize unnecessary code duplication then.
author_association: CONTRIBUTOR
user: login: sankarmanoj-tt
created_at: 2025-05-19T09:05:11Z
body: Tested on Blackhole, and passes.
author_association: CONTRIBUTOR