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