13from pycde.common
import Clock, Input, InputChannel, Output, OutputChannel, Reset
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.
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.
34 if max_outstanding < 1:
35 raise ValueError(
"'max_outstanding' must be at least 1.")
37 counter_width = clog2(max_outstanding + 1)
39 class MaxOutstandingLimiterImpl(Module):
42 in_ = InputChannel(data_type)
44 complete = Input(Bits(1))
45 out = OutputChannel(data_type)
51 complete = ports.complete
54 count = Reg(UInt(counter_width), clk, rst, name=
"outstanding")
57 can_issue = count < UInt(counter_width)(max_outstanding)
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(
65 in_ready.assign(out_ready & can_issue)
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)
78 count.assign(Mux(up, Mux(down, count, count_minus_1), count_plus_1))
80 return MaxOutstandingLimiterImpl