Allow the formal engine to perform a same-cycle result in the ALU
[soc.git] / src / soc / experiment / pi2ls.py
1 """PortInterface to LoadStoreUnitInterface adapter
2
3 PortInterface LoadStoreUnitInterface
4 ------------- ----------------------
5
6 is_ld_i/1 x_ld_i
7 is_st_i/1 x_st_i
8
9 data_len/4 x_mask/16 (translate using LenExpand)
10
11 busy_o/1 most likely to be x_busy_o
12 go_die_i/1 rst?
13 addr.data/64 x_addr_i (x_addr_i[:4] goes into LenExpand)
14 addr.ok/1 probably x_i_valid & ~x_stall_i
15
16 addr_ok_o/1 no equivalent. *might* work using x_stall_i
17 exc_o/6(?) m_load_err_o and m_store_err_o
18
19 ld.data/64 m_ld_data_o
20 ld.ok/1 probably implicit, when x_busy drops low
21 st.data/64 x_st_data_i
22 st.ok/1 probably kinda redundant, set to x_st_i
23 """
24
25 from soc.minerva.units.loadstore import LoadStoreUnitInterface
26 from soc.experiment.pimem import PortInterface
27 from soc.scoreboard.addr_match import LenExpand
28 from soc.experiment.pimem import PortInterfaceBase
29 from nmigen.utils import log2_int
30
31 from nmigen import Elaboratable, Module, Signal
32 from nmutil.latch import SRLatch
33 from nmutil.util import rising_edge
34
35
36
37 class Pi2LSUI(PortInterfaceBase):
38
39 def __init__(self, name, lsui=None,
40 data_wid=64, mask_wid=8, addr_wid=64):
41 print("pi2lsui reg mask addr", data_wid, mask_wid, addr_wid)
42 super().__init__(data_wid, addr_wid)
43 if lsui is None:
44 lsui = LoadStoreUnitInterface(addr_wid, self.addrbits, data_wid)
45 self.lsui = lsui
46 self.lsui_busy = Signal()
47 self.valid_l = SRLatch(False, name="valid")
48
49 def set_wr_addr(self, m, addr, mask, misalign, msr, is_dcbz):
50 print("pi2lsui TODO, implement is_dcbz")
51 m.d.comb += self.valid_l.s.eq(1)
52 m.d.comb += self.lsui.x_mask_i.eq(mask)
53 m.d.comb += self.lsui.x_addr_i.eq(addr)
54
55 def set_rd_addr(self, m, addr, mask, misalign, msr):
56 m.d.comb += self.valid_l.s.eq(1)
57 m.d.comb += self.lsui.x_mask_i.eq(mask)
58 m.d.comb += self.lsui.x_addr_i.eq(addr)
59
60 def set_wr_data(self, m, data, wen): # mask already done in addr setup
61 m.d.comb += self.lsui.x_st_data_i.eq(data)
62 return (~(self.lsui.x_busy_o | self.lsui_busy))
63
64 def get_rd_data(self, m):
65 return self.lsui.m_ld_data_o, ~self.lsui_busy
66
67 def elaborate(self, platform):
68 m = super().elaborate(platform)
69 pi, lsui, addrbits = self.pi, self.lsui, self.addrbits
70
71 m.submodules.valid_l = self.valid_l
72 ld_in_progress = Signal()
73
74 # pass ld/st through to LSUI
75 m.d.comb += lsui.x_ld_i.eq(pi.is_ld_i)
76 m.d.comb += lsui.x_st_i.eq(pi.is_st_i)
77
78 # ooo how annoying. x_busy_o is set synchronously, i.e. one
79 # clock too late for this converter to "notice". consequently,
80 # when trying to wait for ld/st, here: on the first cycle
81 # it goes "oh, x_busy_o isn't set, the ld/st must have been
82 # completed already, we must be done" when in fact it hasn't
83 # started. to "fix" that we actually have to have a full FSM
84 # tracking from when LD/ST starts, right the way through. sigh.
85 # first clock busy signal. needed because x_busy_o is sync
86 with m.FSM() as fsm:
87 with m.State("IDLE"):
88 # detect when ld/st starts. set busy *immediately*
89 with m.If((pi.is_ld_i | pi.is_st_i) & self.valid_l.q):
90 m.d.comb += self.lsui_busy.eq(1)
91 m.next = "BUSY"
92 with m.State("BUSY"):
93 # detect when busy drops: must then wait for ld/st to end..
94 #m.d.comb += self.lsui_busy.eq(self.lsui.x_busy_o)
95 m.d.comb += self.lsui_busy.eq(1)
96 with m.If(~self.lsui.x_busy_o):
97 m.next = "WAITDEASSERT"
98 with m.State("WAITDEASSERT"):
99 # when no longer busy: back to start
100 with m.If(~pi.is_st_i & ~pi.busy_o):
101 m.next = "IDLE"
102
103 # indicate valid at both ends. OR with lsui_busy (stops comb loop)
104 m.d.comb += self.lsui.m_i_valid.eq(self.valid_l.q )
105 m.d.comb += self.lsui.x_i_valid.eq(self.valid_l.q )
106
107 # reset the valid latch when not busy. sync to stop loop
108 lsui_active = Signal()
109 m.d.comb += lsui_active.eq(~self.lsui.x_busy_o)
110 m.d.comb += self.valid_l.r.eq(rising_edge(m, lsui_active))
111
112 return m
113
114
115 class Pi2LSUI1(Elaboratable):
116
117 def __init__(self, name, pi=None, lsui=None,
118 data_wid=64, mask_wid=8, addr_wid=64):
119 print("pi2lsui reg mask addr", data_wid, mask_wid, addr_wid)
120 self.addrbits = mask_wid
121 if pi is None:
122 piname = "%s_pi" % name
123 pi = PortInterface(piname, regwid=data_wid, addrwid=addr_wid)
124 self.pi = pi
125 if lsui is None:
126 lsui = LoadStoreUnitInterface(addr_wid, self.addrbits, data_wid)
127 self.lsui = lsui
128
129 def splitaddr(self, addr):
130 """split the address into top and bottom bits of the memory granularity
131 """
132 return addr[:self.addrbits], addr[self.addrbits:]
133
134 def connect_port(self, inport):
135 return self.pi.connect_port(inport)
136
137 def elaborate(self, platform):
138 m = Module()
139 pi, lsui, addrbits = self.pi, self.lsui, self.addrbits
140 m.submodules.lenexp = lenexp = LenExpand(log2_int(self.addrbits), 8)
141
142 ld_in_progress = Signal(reset=0)
143 st_in_progress = Signal(reset=0)
144
145 m.d.comb += lsui.x_ld_i.eq(pi.is_ld_i)
146 m.d.comb += lsui.x_st_i.eq(pi.is_st_i)
147 m.d.comb += pi.busy_o.eq(pi.is_ld_i | pi.is_st_i) # lsui.x_busy_o)
148
149 lsbaddr, msbaddr = self.splitaddr(pi.addr.data)
150 m.d.comb += lenexp.len_i.eq(pi.data_len)
151 m.d.comb += lenexp.addr_i.eq(lsbaddr) # LSBs of addr
152 m.d.comb += lsui.x_addr_i.eq(pi.addr.data) # XXX hmmm...
153
154 with m.If(pi.addr.ok):
155 # expand the LSBs of address plus LD/ST len into 16-bit mask
156 m.d.comb += lsui.x_mask_i.eq(lenexp.lexp_o)
157 # pass through the address, indicate "valid"
158 m.d.comb += lsui.x_i_valid.eq(1)
159 # indicate "OK" - XXX should be checking address valid
160 m.d.comb += pi.addr_ok_o.eq(1)
161
162 with m.If(~lsui.x_busy_o & pi.is_st_i & pi.addr.ok):
163 m.d.sync += st_in_progress.eq(1)
164
165 with m.If(pi.is_ld_i):
166 # shift/mask out the loaded data
167 m.d.comb += pi.ld.data.eq((lsui.m_ld_data_o & lenexp.rexp_o) >>
168 (lenexp.addr_i*8))
169 # remember we're in the process of loading
170 with m.If(pi.addr.ok):
171 m.d.sync += ld_in_progress.eq(1)
172
173 # If a load happened on the previous cycle and the memory is
174 # not busy, that means it returned the data from the load. In
175 # that case ld.ok should be set andwe can clear the
176 # ld_in_progress flag
177 with m.If(ld_in_progress & ~lsui.x_busy_o):
178 m.d.comb += pi.ld.ok.eq(1)
179 m.d.sync += ld_in_progress.eq(0)
180 with m.Else():
181 m.d.comb += pi.ld.ok.eq(0)
182
183 with m.If(pi.is_st_i & pi.st.ok):
184 m.d.comb += lsui.x_st_data_i.eq(pi.st.data << (lenexp.addr_i*8))
185 with m.If(st_in_progress):
186 m.d.sync += st_in_progress.eq(0)
187 with m.Else():
188 m.d.comb += pi.busy_o.eq(0)
189
190 return m