CIRCT 23.0.0git
Loading...
Searching...
No Matches
flow.py
Go to the documentation of this file.
1# ===- flow.py - channel flow-control primitives ---------------------------===//
2#
3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4# See https://llvm.org/LICENSE.txt for license information.
5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6#
7# ===-----------------------------------------------------------------------===//
8#
9# Flow-control building blocks for ESI channels.
10#
11# ===-----------------------------------------------------------------------===//
12
13from pycde.common import Clock, Input, InputChannel, Output, OutputChannel, Reset
14from pycde.constructs import Mux, Reg, Wire
15from pycde.module import Module, generator, modparams
16from pycde.support import clog2
17from pycde.types import Bits, Type, UInt
18
19
20@modparams
21def MaxOutstandingLimiter(
22 data_type: Type, max_outstanding: int) -> type["MaxOutstandingLimiterImpl"]:
23 """Rate-limit a channel to at most `max_outstanding` outstanding
24 transactions. A transaction becomes outstanding when a message is accepted
25 on `in_` and remains outstanding until `complete` is pulsed. `in_` is
26 stalled whenever `max_outstanding` transactions are already outstanding.
27
28 `complete` must pulse for exactly one cycle per completed transaction and
29 must not pulse while zero transactions are outstanding; the module does not
30 check this. When an input transaction and a `complete` pulse occur on the
31 same cycle the outstanding count is left unchanged.
32 """
33
34 if max_outstanding < 1:
35 raise ValueError("'max_outstanding' must be at least 1.")
36
37 counter_width = clog2(max_outstanding + 1)
38
39 class MaxOutstandingLimiterImpl(Module):
40 clk = Clock()
41 rst = Reset()
42 in_ = InputChannel(data_type)
43 # One-cycle pulse per completed transaction.
44 complete = Input(Bits(1))
45 out = OutputChannel(data_type)
46
47 @generator
48 def build(ports):
49 clk = ports.clk
50 rst = ports.rst
51 complete = ports.complete
52
53 # Registered count of outstanding transactions.
54 count = Reg(UInt(counter_width), clk, rst, name="outstanding")
55
56 # Only accept a new transaction while below the limit.
57 can_issue = count < UInt(counter_width)(max_outstanding)
58
59 # Forward the input channel to the output, gated by 'can_issue'.
60 in_ready = Wire(Bits(1))
61 in_data, in_valid = ports.in_.unwrap(in_ready)
62 out_valid = (in_valid & can_issue).as_bits()
63 out_chan, out_ready = MaxOutstandingLimiterImpl.out.type.wrap(
64 in_data, out_valid)
65 in_ready.assign(out_ready & can_issue)
66 ports.out = out_chan
67
68 # +1 on an accepted input, -1 on a 'complete' pulse. If both occur on
69 # the same cycle the count is unchanged. 'can_issue' guarantees the
70 # counter never overflows; the caller guarantees it never underflows.
71 issue = out_valid & out_ready
72 up = issue & ~complete
73 down = complete & ~issue
74 one = UInt(counter_width)(1)
75 count_plus_1 = (count + one).as_uint(counter_width)
76 count_minus_1 = (count - one).as_uint(counter_width)
77 # up=1 -> count_plus_1; up=0, down=1 -> count_minus_1; else count.
78 count.assign(Mux(up, Mux(down, count, count_minus_1), count_plus_1))
79
80 return MaxOutstandingLimiterImpl