resolve WBDownConvert ack issues when stall is active
[soc.git] / src / soc / bus / wb_downconvert.py
1 from nmigen import Elaboratable, Module, Signal, Repl, Cat, Mux
2 from nmigen.utils import log2_int
3
4 class WishboneDownConvert(Elaboratable):
5 """DownConverter
6
7 This module splits Wishbone accesses from a master interface to a smaller
8 slave interface.
9
10 Writes:
11 Writes from master are split N writes to the slave. Access
12 is acked when the last access is acked by the slave.
13
14 Reads:
15 Read from master are split in N reads to the the slave.
16 Read data from the slave are cached before being presented,
17 concatenated on the last access.
18
19 TODO:
20 Manage err signal? (Not implemented since we generally don't
21 use it on Migen/MiSoC modules)
22 """
23 def __init__(self, master, slave):
24 self.master = master
25 self.slave = slave
26
27 def elaborate(self, platform):
28
29 master = self.master
30 slave = self.slave
31 m = Module()
32 comb = m.d.comb
33 sync = m.d.sync
34
35 dw_from = len(master.dat_r)
36 dw_to = len(slave.dat_w)
37 ratio = dw_from//dw_to
38
39 print ("wb downconvert from to ratio", dw_from, dw_to, ratio)
40
41 # # #
42
43 read = Signal()
44 write = Signal()
45 dat_r = Signal(dw_from)
46
47 counter = Signal(log2_int(ratio, False))
48 counter_reset = Signal()
49 counter_ce = Signal()
50 with m.If(counter_reset):
51 sync += counter.eq(0)
52 with m.Elif(counter_ce):
53 sync += counter.eq(counter + 1)
54
55 counter_done = Signal()
56 comb += counter_done.eq(counter == ratio-1)
57
58 # Main FSM
59 with m.FSM() as fsm:
60 with m.State("IDLE"):
61 comb += counter_reset.eq(1)
62 sync += dat_r.eq(0)
63 sync += master.ack.eq(0)
64 with m.If(master.stb & master.cyc):
65 if hasattr(master, 'stall'):
66 comb += master.stall.eq(1)
67 with m.If(master.we):
68 m.next = "WRITE"
69 with m.Else():
70 m.next = "READ"
71
72 with m.State("WRITE"):
73 comb += write.eq(1)
74 comb += slave.we.eq(1)
75 comb += slave.cyc.eq(1)
76 with m.If(master.stb & master.cyc):
77 if hasattr(master, 'stall'):
78 comb += master.stall.eq(1)
79 if hasattr(slave, 'stall'):
80 with m.If(~slave.stall):
81 comb += slave.stb.eq(1)
82 else:
83 comb += slave.stb.eq(1)
84 with m.If(slave.ack):
85 comb += counter_ce.eq(1)
86 with m.If(counter_done):
87 if hasattr(master, 'stall'):
88 comb += master.stall.eq(0)
89 sync += master.ack.eq(1)
90 m.next = "IDLE"
91 with m.Elif(~master.cyc):
92 m.next = "IDLE"
93
94 with m.State("READ"):
95 comb += read.eq(1)
96 comb += slave.cyc.eq(1)
97 with m.If(master.stb & master.cyc):
98 if hasattr(master, 'stall'):
99 comb += master.stall.eq(1)
100 if hasattr(slave, 'stall'):
101 with m.If(~slave.stall):
102 comb += slave.stb.eq(1)
103 else:
104 comb += slave.stb.eq(1)
105 with m.If(slave.ack):
106 comb += counter_ce.eq(1)
107 with m.If(counter_done):
108 if hasattr(master, 'stall'):
109 comb += master.stall.eq(0)
110 sync += master.ack.eq(1)
111 m.next = "IDLE"
112 with m.Elif(~master.cyc):
113 m.next = "IDLE"
114
115 # Address
116 if hasattr(slave, 'cti'):
117 with m.If(counter_done):
118 comb += slave.cti.eq(7) # indicate end of burst
119 with m.Else():
120 comb += slave.cti.eq(2)
121 comb += slave.adr.eq(Cat(counter, master.adr))
122
123 # write Datapath - select fragments of data, depending on "counter"
124 with m.Switch(counter):
125 slen = slave.sel.width
126 for i in range(ratio):
127 with m.Case(i):
128 # select fractions of dat_w and associated "sel" bits
129 print ("sel", i, "from", i*slen, "to", (i+1)*slen)
130 comb += slave.sel.eq(master.sel[i*slen:(i+1)*slen])
131 comb += slave.dat_w.eq(master.dat_w[i*dw_to:(i+1)*dw_to])
132
133 # read Datapath - uses cached_data and master.dat_r as a shift-register.
134 # by the time "counter" is done (counter_done) this is complete
135 comb += master.dat_r.eq(Cat(dat_r[dw_to:], slave.dat_r))
136 with m.If(read & slave.ack):
137 sync += dat_r.eq(master.dat_r)
138
139 return m