add wishbone downconvert "skip" of slave sel so that action
[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
46 cached_data = Signal(dw_from)
47 shift_reg = Signal(dw_from)
48
49 counter = Signal(log2_int(ratio, False))
50 cur_counter = Signal(log2_int(ratio, False))
51
52 counter_done = Signal()
53 comb += counter_done.eq(counter == ratio-1)
54 comb += cur_counter.eq(counter)
55 skip = Signal()
56
57 # Main FSM
58 with m.FSM() as fsm:
59 with m.State("IDLE"):
60 sync += counter.eq(0)
61 sync += cached_data.eq(0)
62 with m.If(master.stb & master.cyc):
63 with m.If(master.we):
64 m.next = "WRITE"
65 with m.Else():
66 m.next = "READ"
67
68 with m.State("WRITE"):
69 comb += write.eq(1)
70 with m.If(master.stb & master.cyc):
71 comb += skip.eq(slave.sel == 0)
72 comb += slave.we.eq(1)
73 comb += slave.cyc.eq(1)
74 comb += slave.stb.eq(1)
75 with m.If(slave.ack | skip):
76 comb += cur_counter.eq(counter + 1) # TODO use Picker
77 sync += counter.eq(cur_counter)
78 with m.If(counter_done):
79 comb += master.ack.eq(1)
80 m.next = "IDLE"
81 with m.Elif(~master.cyc):
82 m.next = "IDLE"
83
84 with m.State("READ"):
85 comb += read.eq(1)
86 with m.If(master.stb & master.cyc):
87 comb += skip.eq(slave.sel == 0)
88 comb += slave.cyc.eq(1)
89 comb += slave.stb.eq(1)
90 with m.If(slave.ack | skip):
91 comb += cur_counter.eq(counter + 1) # TODO use Picker
92 sync += counter.eq(cur_counter)
93 with m.If(counter_done):
94 comb += master.ack.eq(1)
95 comb += master.dat_r.eq(shift_reg)
96 m.next = "IDLE"
97 with m.Elif(~master.cyc):
98 m.next = "IDLE"
99
100 # Address
101 if hasattr(slave, 'cti'):
102 with m.If(counter_done):
103 comb += slave.cti.eq(7) # indicate end of burst
104 with m.Else():
105 comb += slave.cti.eq(2)
106 comb += slave.adr.eq(Cat(cur_counter, master.adr))
107
108 # write Datapath - select fragments of data, depending on "counter"
109 with m.Switch(counter):
110 slen = slave.sel.width
111 for i in range(ratio):
112 with m.Case(i):
113 # select fractions of dat_w and associated "sel" bits
114 print ("sel", i, "from", i*slen, "to", (i+1)*slen)
115 comb += slave.sel.eq(master.sel[i*slen:(i+1)*slen])
116 comb += slave.dat_w.eq(master.dat_w[i*dw_to:(i+1)*dw_to])
117
118 # read Datapath - uses cached_data and master.dat_r as a shift-register.
119 # by the time "counter" is done (counter_done) this is complete
120 comb += shift_reg.eq(Cat(cached_data[dw_to:], slave.dat_r))
121 with m.If(read & (slave.ack | skip)):
122 sync += cached_data.eq(shift_reg)
123
124
125 return m