link up output from ld1/ld2 and merge into request
[soc.git] / src / soc / scoreboard / addr_split.py
1 # LDST Address Splitter. For misaligned address crossing cache line boundary
2
3 from nmigen import Elaboratable, Module, Signal, Record, Array
4 from nmutil.latch import SRLatch, latchregister
5 from nmigen.compat.sim import run_simulation
6 from nmigen.cli import verilog, rtlil
7
8 from soc.scoreboard.addr_match import LenExpand
9 #from nmutil.queue import Queue
10
11 class LDData(Record):
12 def __init__(self, dwidth, name=None):
13 Record.__init__(self, (('err', 1), ('data', dwidth)), name=name)
14
15
16 class LDLatch(Elaboratable):
17
18 def __init__(self, dwidth, awidth, mlen):
19 self.addr_i = Signal(awidth, reset_less=True)
20 self.mask_i = Signal(mlen, reset_less=True)
21 self.valid_i = Signal(reset_less=True)
22 self.ld_i = LDData(dwidth, "ld_i")
23 self.ld_o = LDData(dwidth, "ld_o")
24 self.valid_o = Signal(reset_less=True)
25
26 def elaborate(self, platform):
27 m = Module()
28 comb = m.d.comb
29 m.submodules.in_l = in_l = SRLatch(sync=False, name="in_l")
30
31 comb += self.valid_o.eq(in_l.q & self.valid_i)
32 latchregister(m, self.ld_i, self.ld_o, in_l.q & self.valid_o, "ld_i_r")
33
34 return m
35
36
37 class LDSTSplitter(Elaboratable):
38
39 def __init__(self, dwidth, awidth, dlen):
40 self.dwidth, self.awidth, self.dlen = dwidth, awidth, dlen
41 self.addr_i = Signal(awidth, reset_less=True)
42 self.len_i = Signal(dlen, reset_less=True)
43 self.is_ld_i = Signal(reset_less=True)
44 self.ld_data_o = LDData(dwidth, "ld_data_o")
45 self.ld_valid_i = Signal(reset_less=True)
46 self.valid_o = Signal(2, reset_less=True)
47 self.ld_data_i = Array((LDData(dwidth, "ld_data_i1"),
48 LDData(dwidth, "ld_data_i2")))
49
50 #self.is_st_i = Signal(reset_less=True)
51 #self.st_data_i = Signal(dwidth, reset_less=True)
52
53 def elaborate(self, platform):
54 m = Module()
55 comb = m.d.comb
56 dlen = self.dlen
57 mlen = 1 << dlen
58 m.submodules.ld1 = ld1 = LDLatch(self.dwidth, self.awidth-dlen, mlen)
59 m.submodules.ld2 = ld2 = LDLatch(self.dwidth, self.awidth-dlen, mlen)
60 m.submodules.lenexp = lenexp = LenExpand(self.dlen)
61
62 # set up len-expander, len to mask. ld1 gets first bit, ld2 gets rest
63 comb += lenexp.addr_i.eq(self.addr_i)
64 comb += lenexp.len_i.eq(self.len_i)
65 mask1 = lenexp.lexp_o[0:mlen] # Lo bits of expanded len-mask
66 mask2 = lenexp.lexp_o[mlen:] # Hi bits of expanded len-mask
67
68 # set up new address records: addr1 is "as-is", addr2 is +1
69 comb += ld1.addr_i.eq(self.addr_i[dlen:])
70 comb += ld2.addr_i.eq(self.addr_i[dlen:] + 1) # TODO exception if rolls
71
72 # set up connections to LD-split. note: not active if mask is zero
73 for i, (ld, mask) in enumerate(((ld1, mask1),
74 (ld2, mask2))):
75 comb += ld.valid_i.eq(self.ld_valid_i)
76 comb += ld.ld_i.eq(self.ld_data_i[i])
77 comb += self.valid_o[i].eq(ld.valid_o & (mask != 0))
78
79 # all bits valid (including when a data error occurs!) decode ld1/ld2
80 with m.If(self.valid_o.all()):
81 # errors cause error condition
82 comb += self.ld_data_o.err.eq(ld1.ld_o.err | ld2.ld_o.err)
83 # data needs recombining via shifting.
84 ashift1 = self.addr_i[:self.dlen]
85 # note that data from LD1 will be in *cache-line* byte position
86 # likewise from LD2 but we *know* it is at the start of the line
87 comb += self.ld_data_o.data.eq((ld1.ld_o.data >> ashift1) |
88 (ld2.ld_o.data << (1<<self.dlen)))
89
90 return m
91
92 def __iter__(self):
93 yield self.addr_i
94 yield self.len_i
95 yield self.is_ld_i
96 yield self.ld_data_o.err
97 yield self.ld_data_o.data
98 yield self.ld_valid_i
99 yield self.valid_o
100 for i in range(2):
101 yield self.ld_data_i[i].err
102 yield self.ld_data_i[i].data
103
104 def ports(self):
105 return list(self)
106
107
108 if __name__ == '__main__':
109 dut = LDSTSplitter(32, 48, 3)
110 vl = rtlil.convert(dut, ports=dut.ports())
111 with open("ldst_splitter.il", "w") as f:
112 f.write(vl)
113