Skip to content

Lower snitch

lower_snitch

Rewrite patterns for lowering snitch → riscv.

SnitchStreamerDimension dataclass

Source code in xdsl/transforms/lower_snitch.py
23
24
25
26
27
28
29
30
31
32
33
34
35
@dataclass(frozen=True)
class SnitchStreamerDimension:
    # Offset of the streamer bound configuration for a specific dimension
    bound: int

    # Offset of the streamer stride configuration for a specific dimension
    stride: int

    # Offset of the streamer source address configuration for a specific dimension
    source: int

    # Offset of the streamer source address configuration for a specific dimension
    destination: int

bound: int instance-attribute

stride: int instance-attribute

source: int instance-attribute

destination: int instance-attribute

__init__(bound: int, stride: int, source: int, destination: int) -> None

SnitchStreamerMemoryMap dataclass

In the Snitch architecture, each streamer (a.k.a. data mover) is configured via a memory-mapped address space that can be written via custom riscv.scfgw (Stream ConFiGure Write) operation. For each streamer we have:

  • Repeat: how many times a value should be repeated when popped from/pushed to a stream
  • Dimensions: a list of supported streaming dimensions

For each dimension, the supported configuration parameters are:

  • Bound
  • Stride
  • Source: base address when reading from a stream
  • Destination: base address when writing to a stream

This table encodes the base addresses for each of the configuration parameters above.

Source code in xdsl/transforms/lower_snitch.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
@dataclass(frozen=True)
class SnitchStreamerMemoryMap:
    """
    In the Snitch architecture, each streamer (a.k.a. data mover)
    is configured via a memory-mapped address space that can be written
    via custom riscv.scfgw (Stream ConFiGure Write) operation. For each
    streamer we have:

    * Repeat: how many times a value should be repeated when
              popped from/pushed to a stream
    * Dimensions: a list of supported streaming dimensions

    For each dimension, the supported configuration parameters are:

    * Bound
    * Stride
    * Source: base address when reading from a stream
    * Destination: base address when writing to a stream

    This table encodes the base addresses for each of the configuration
    parameters above.
    """

    # Global streaming behaviour enable/disable register.
    # Accessible as a regular RISC-V CSR.
    csr: int = 0x7C0

    # Offset of the streamer repetition configuration.
    repeat: int = 0x01

    dimension: tuple[SnitchStreamerDimension, ...] = (
        # Dimension 0
        SnitchStreamerDimension(
            0x02,  # Bound
            0x06,  # Stride
            0x18,  # Source
            0x1C,  # Destination
        ),
        # Dimension 1
        SnitchStreamerDimension(
            0x03,  # Bound
            0x07,  # Stride
            0x19,  # Source
            0x1D,  # Destination
        ),
        # Dimension 2
        SnitchStreamerDimension(
            0x04,  # Bound
            0x08,  # Stride
            0x1A,  # Source
            0x1E,  # Destination
        ),
        # Dimension 3
        SnitchStreamerDimension(
            0x05,  # Bound
            0x09,  # Stride
            0x1B,  # Source
            0x1F,  # Destination
        ),
    )

csr: int = 1984 class-attribute instance-attribute

repeat: int = 1 class-attribute instance-attribute

dimension: tuple[SnitchStreamerDimension, ...] = (SnitchStreamerDimension(2, 6, 24, 28), SnitchStreamerDimension(3, 7, 25, 29), SnitchStreamerDimension(4, 8, 26, 30), SnitchStreamerDimension(5, 9, 27, 31)) class-attribute instance-attribute

__init__(csr: int = 1984, repeat: int = 1, dimension: tuple[SnitchStreamerDimension, ...] = (SnitchStreamerDimension(2, 6, 24, 28), SnitchStreamerDimension(3, 7, 25, 29), SnitchStreamerDimension(4, 8, 26, 30), SnitchStreamerDimension(5, 9, 27, 31))) -> None

LowerSsrSetDimensionBoundOp

Bases: RewritePattern

Source code in xdsl/transforms/lower_snitch.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
class LowerSsrSetDimensionBoundOp(RewritePattern):
    @op_type_rewrite_pattern
    def match_and_rewrite(
        self, op: snitch.SsrSetDimensionBoundOp, rewriter: PatternRewriter, /
    ):
        dim: int = op.dimension.data
        ops = write_ssr_config_ops(
            dm=op.dm.data,
            reg=SnitchStreamerMemoryMap.dimension[dim].bound,
            value=op.value,
            comment=f"dm {op.dm.data} dim {dim} bound",
        )
        rewriter.replace_op(
            op,
            [*ops],
            [],
        )

match_and_rewrite(op: snitch.SsrSetDimensionBoundOp, rewriter: PatternRewriter)

Source code in xdsl/transforms/lower_snitch.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@op_type_rewrite_pattern
def match_and_rewrite(
    self, op: snitch.SsrSetDimensionBoundOp, rewriter: PatternRewriter, /
):
    dim: int = op.dimension.data
    ops = write_ssr_config_ops(
        dm=op.dm.data,
        reg=SnitchStreamerMemoryMap.dimension[dim].bound,
        value=op.value,
        comment=f"dm {op.dm.data} dim {dim} bound",
    )
    rewriter.replace_op(
        op,
        [*ops],
        [],
    )

LowerSsrSetDimensionStrideOp

Bases: RewritePattern

Source code in xdsl/transforms/lower_snitch.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
class LowerSsrSetDimensionStrideOp(RewritePattern):
    @op_type_rewrite_pattern
    def match_and_rewrite(
        self, op: snitch.SsrSetDimensionStrideOp, rewriter: PatternRewriter, /
    ):
        dim: int = op.dimension.data
        ops = write_ssr_config_ops(
            dm=op.dm.data,
            reg=SnitchStreamerMemoryMap.dimension[dim].stride,
            value=op.value,
            comment=f"dm {op.dm.data} dim {dim} stride",
        )
        rewriter.replace_op(
            op,
            [*ops],
            [],
        )

match_and_rewrite(op: snitch.SsrSetDimensionStrideOp, rewriter: PatternRewriter)

Source code in xdsl/transforms/lower_snitch.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
@op_type_rewrite_pattern
def match_and_rewrite(
    self, op: snitch.SsrSetDimensionStrideOp, rewriter: PatternRewriter, /
):
    dim: int = op.dimension.data
    ops = write_ssr_config_ops(
        dm=op.dm.data,
        reg=SnitchStreamerMemoryMap.dimension[dim].stride,
        value=op.value,
        comment=f"dm {op.dm.data} dim {dim} stride",
    )
    rewriter.replace_op(
        op,
        [*ops],
        [],
    )

LowerSsrSetDimensionSourceOp

Bases: RewritePattern

Source code in xdsl/transforms/lower_snitch.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
class LowerSsrSetDimensionSourceOp(RewritePattern):
    @op_type_rewrite_pattern
    def match_and_rewrite(
        self, op: snitch.SsrSetDimensionSourceOp, rewriter: PatternRewriter, /
    ):
        dim: int = op.dimension.data
        ops = write_ssr_config_ops(
            dm=op.dm.data,
            reg=SnitchStreamerMemoryMap.dimension[dim].source,
            value=op.value,
            comment=f"dm {op.dm.data} dim {dim} source",
        )
        rewriter.replace_op(
            op,
            [*ops],
            [],
        )

match_and_rewrite(op: snitch.SsrSetDimensionSourceOp, rewriter: PatternRewriter)

Source code in xdsl/transforms/lower_snitch.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
@op_type_rewrite_pattern
def match_and_rewrite(
    self, op: snitch.SsrSetDimensionSourceOp, rewriter: PatternRewriter, /
):
    dim: int = op.dimension.data
    ops = write_ssr_config_ops(
        dm=op.dm.data,
        reg=SnitchStreamerMemoryMap.dimension[dim].source,
        value=op.value,
        comment=f"dm {op.dm.data} dim {dim} source",
    )
    rewriter.replace_op(
        op,
        [*ops],
        [],
    )

LowerSsrSetDimensionDestinationOp

Bases: RewritePattern

Source code in xdsl/transforms/lower_snitch.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
class LowerSsrSetDimensionDestinationOp(RewritePattern):
    @op_type_rewrite_pattern
    def match_and_rewrite(
        self, op: snitch.SsrSetDimensionDestinationOp, rewriter: PatternRewriter, /
    ):
        dim: int = op.dimension.data
        ops = write_ssr_config_ops(
            dm=op.dm.data,
            reg=SnitchStreamerMemoryMap.dimension[dim].destination,
            value=op.value,
            comment=f"dm {op.dm.data} dim {dim} destination",
        )
        rewriter.replace_op(
            op,
            [*ops],
            [],
        )

match_and_rewrite(op: snitch.SsrSetDimensionDestinationOp, rewriter: PatternRewriter)

Source code in xdsl/transforms/lower_snitch.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
@op_type_rewrite_pattern
def match_and_rewrite(
    self, op: snitch.SsrSetDimensionDestinationOp, rewriter: PatternRewriter, /
):
    dim: int = op.dimension.data
    ops = write_ssr_config_ops(
        dm=op.dm.data,
        reg=SnitchStreamerMemoryMap.dimension[dim].destination,
        value=op.value,
        comment=f"dm {op.dm.data} dim {dim} destination",
    )
    rewriter.replace_op(
        op,
        [*ops],
        [],
    )

LowerSsrSetStreamRepetitionOp

Bases: RewritePattern

Source code in xdsl/transforms/lower_snitch.py
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
class LowerSsrSetStreamRepetitionOp(RewritePattern):
    @op_type_rewrite_pattern
    def match_and_rewrite(
        self, op: snitch.SsrSetStreamRepetitionOp, rewriter: PatternRewriter, /
    ):
        ops = write_ssr_config_ops(
            dm=op.dm.data,
            reg=SnitchStreamerMemoryMap.repeat,
            value=op.value,
            comment=f"dm {op.dm.data} repeat",
        )
        rewriter.replace_op(
            op,
            [*ops],
            [],
        )

match_and_rewrite(op: snitch.SsrSetStreamRepetitionOp, rewriter: PatternRewriter)

Source code in xdsl/transforms/lower_snitch.py
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
@op_type_rewrite_pattern
def match_and_rewrite(
    self, op: snitch.SsrSetStreamRepetitionOp, rewriter: PatternRewriter, /
):
    ops = write_ssr_config_ops(
        dm=op.dm.data,
        reg=SnitchStreamerMemoryMap.repeat,
        value=op.value,
        comment=f"dm {op.dm.data} repeat",
    )
    rewriter.replace_op(
        op,
        [*ops],
        [],
    )

LowerSsrEnable

Bases: RewritePattern

Source code in xdsl/transforms/lower_snitch.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
class LowerSsrEnable(RewritePattern):
    @op_type_rewrite_pattern
    def match_and_rewrite(self, op: snitch.SsrEnableOp, rewriter: PatternRewriter, /):
        get_stream_ops = tuple(riscv_snitch.GetStreamOp(res.type) for res in op.results)
        rewriter.replace_op(
            op,
            [
                riscv.CsrrsiOp(
                    csr=IntegerAttr(SnitchStreamerMemoryMap.csr, i32),
                    immediate=IntegerAttr(1, i32),
                    rd=riscv.Registers.ZERO,
                    comment="SSR enable",
                ),
                *get_stream_ops,
            ],
            tuple(op.stream for op in get_stream_ops),
        )

match_and_rewrite(op: snitch.SsrEnableOp, rewriter: PatternRewriter)

Source code in xdsl/transforms/lower_snitch.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
@op_type_rewrite_pattern
def match_and_rewrite(self, op: snitch.SsrEnableOp, rewriter: PatternRewriter, /):
    get_stream_ops = tuple(riscv_snitch.GetStreamOp(res.type) for res in op.results)
    rewriter.replace_op(
        op,
        [
            riscv.CsrrsiOp(
                csr=IntegerAttr(SnitchStreamerMemoryMap.csr, i32),
                immediate=IntegerAttr(1, i32),
                rd=riscv.Registers.ZERO,
                comment="SSR enable",
            ),
            *get_stream_ops,
        ],
        tuple(op.stream for op in get_stream_ops),
    )

LowerSsrDisable

Bases: RewritePattern

Source code in xdsl/transforms/lower_snitch.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
class LowerSsrDisable(RewritePattern):
    @op_type_rewrite_pattern
    def match_and_rewrite(self, op: snitch.SsrDisableOp, rewriter: PatternRewriter, /):
        rewriter.replace_op(
            op,
            [
                riscv.CsrrciOp(
                    csr=IntegerAttr(SnitchStreamerMemoryMap.csr, i32),
                    immediate=IntegerAttr(1, i32),
                    rd=riscv.Registers.ZERO,
                    comment="SSR disable",
                )
            ],
            [],
        )

match_and_rewrite(op: snitch.SsrDisableOp, rewriter: PatternRewriter)

Source code in xdsl/transforms/lower_snitch.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256
@op_type_rewrite_pattern
def match_and_rewrite(self, op: snitch.SsrDisableOp, rewriter: PatternRewriter, /):
    rewriter.replace_op(
        op,
        [
            riscv.CsrrciOp(
                csr=IntegerAttr(SnitchStreamerMemoryMap.csr, i32),
                immediate=IntegerAttr(1, i32),
                rd=riscv.Registers.ZERO,
                comment="SSR disable",
            )
        ],
        [],
    )

LowerSnitchPass dataclass

Bases: ModulePass

Source code in xdsl/transforms/lower_snitch.py
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
@dataclass(frozen=True)
class LowerSnitchPass(ModulePass):
    name = "lower-snitch"

    def apply(self, ctx: Context, op: builtin.ModuleOp) -> None:
        walker = PatternRewriteWalker(
            GreedyRewritePatternApplier(
                [
                    LowerSsrSetDimensionBoundOp(),
                    LowerSsrSetDimensionStrideOp(),
                    LowerSsrSetDimensionSourceOp(),
                    LowerSsrSetDimensionDestinationOp(),
                    LowerSsrSetStreamRepetitionOp(),
                    LowerSsrEnable(),
                    LowerSsrDisable(),
                ]
            ),
            apply_recursively=False,
        )
        walker.rewrite_module(op)

name = 'lower-snitch' class-attribute instance-attribute

__init__() -> None

apply(ctx: Context, op: builtin.ModuleOp) -> None

Source code in xdsl/transforms/lower_snitch.py
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
def apply(self, ctx: Context, op: builtin.ModuleOp) -> None:
    walker = PatternRewriteWalker(
        GreedyRewritePatternApplier(
            [
                LowerSsrSetDimensionBoundOp(),
                LowerSsrSetDimensionStrideOp(),
                LowerSsrSetDimensionSourceOp(),
                LowerSsrSetDimensionDestinationOp(),
                LowerSsrSetStreamRepetitionOp(),
                LowerSsrEnable(),
                LowerSsrDisable(),
            ]
        ),
        apply_recursively=False,
    )
    walker.rewrite_module(op)

write_ssr_config_ops(reg: int, dm: int, value: Operand, comment: str | None = None) -> Iterable[Operation]

Return the list of riscv operations needed to set a specific SSR configuration
parameter located at 'reg' to a specific 'value' for a specific data mover
identified by 'dm'.

To compute the actual address of the memory-mapped configuration parameter,
we have to compute:

address = dm + reg << 5

This value is then passed to riscv.scfgw to perform the actual setting.

Reference implementation in the snitch runtime library:
```C
inline void write_ssr_cfg(uint32_t reg, uint32_t dm, uint32_t value) {
    asm volatile("scfgwi %[value], %[dm] | %[reg]<<5

" ::[value] "r"(value), [ dm ] "i"(dm), [ reg ] "i"(reg)); } ```

Source code in xdsl/transforms/lower_snitch.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def write_ssr_config_ops(
    reg: int, dm: int, value: Operand, comment: str | None = None
) -> Iterable[Operation]:
    """
    Return the list of riscv operations needed to set a specific SSR configuration
    parameter located at 'reg' to a specific 'value' for a specific data mover
    identified by 'dm'.

    To compute the actual address of the memory-mapped configuration parameter,
    we have to compute:

    address = dm + reg << 5

    This value is then passed to riscv.scfgw to perform the actual setting.

    Reference implementation in the snitch runtime library:
    ```C
    inline void write_ssr_cfg(uint32_t reg, uint32_t dm, uint32_t value) {
        asm volatile("scfgwi %[value], %[dm] | %[reg]<<5\n" ::[value] "r"(value),
                    [ dm ] "i"(dm), [ reg ] "i"(reg));
    }
    ```
    """
    return [
        address := riscv.LiOp(immediate=IntegerAttr(dm | reg << 5, i32)),
        riscv_snitch.ScfgwOp(rs1=value, rs2=address, comment=comment),
    ]