3 This first version is intended for prototyping and test purposes:
4 it has "direct" access to Memory.
6 The intention is that this version remains an integral part of the
7 test infrastructure, and, just as with minerva's memory arrangement,
8 a dynamic runtime config *selects* alternative memory arrangements
9 rather than *replaces and discards* this code.
13 from nmigen
.compat
.sim
import run_simulation
14 from nmigen
.cli
import verilog
, rtlil
15 from nmigen
import Module
, Signal
, Mux
, Elaboratable
, Array
16 from nmutil
.iocontrol
import RecordObject
18 from nmutil
.latch
import SRLatch
, latchregister
19 from soc
.decoder
.power_decoder2
import Data
20 from soc
.decoder
.power_enums
import InternalOp
22 from soc
.experiment
.compldst
import CompLDSTOpSubset
23 from soc
.decoder
.power_decode2
import Data
24 from nmutil
.picker
import PriorityPicker
27 class PortInterface(RecordObject
):
30 defines the interface - the API - that the LDSTCompUnit connects
31 to. note that this is NOT a "fire-and-forget" interface. the
32 LDSTCompUnit *must* be kept appraised that the request is in
33 progress, and only when it has a 100% successful completion rate
34 can the notification be given (busy dropped).
36 The interface FSM rules are as follows:
38 * if busy_o is asserted, a LD/ST is in progress. further
39 requests may not be made until busy_o is deasserted.
41 * only one of is_ld_i or is_st_i may be asserted. busy_o
42 will immediately be asserted and remain asserted.
44 * addr.ok is to be asserted when the LD/ST address is known
45 * addr_ok_o (or addr_exc_o) must be waited for. these will
46 be asserted *only* for one cycle and one cycle only.
48 * addr_exc_o will be asserted if there is no chance that the
49 memory request may be fulfilled.
51 busy_o is deasserted on the same cycle as addr_exc_o is asserted.
53 * conversely: addr_ok_o must *ONLY* be asserted if there is a
54 HUNDRED PERCENT guarantee that the memory request will be
57 * for a LD, ld.ok will be asserted - for only one clock cycle -
58 at any point in the future that is acceptable to the underlying
59 Memory subsystem. the recipient MUST latch ld.data on that cycle.
61 busy_o is deasserted on the same cycle as ld.ok is asserted.
63 * for a ST, st.ok may be asserted only after addr_ok_o had been
64 asserted, alongside valid st.data at the same time. st.ok
65 must only be asserted for one cycle.
67 the underlying Memory is REQUIRED to pick up that data and
68 guarantee its delivery. no back-acknowledgement is required.
70 busy_o is deasserted on the same cycle as ld.ok is asserted.
73 def __init__(self
, name
=None):
75 RecordObject
.__init
__(self
, name
=name
)
77 # distinguish op type (ld/st)
78 self
.is_ld_i
= Signal(reset_less
=True)
79 self
.is_st_i
= Signal(reset_less
=True)
80 self
.op
= CompLDSTOpSubset() # hm insn_type ld/st duplicates here
83 self
.busy_o
= Signal(reset_less
=True) # do not use if busy
84 self
.go_die_i
= Signal(reset_less
=True) # back to reset
85 self
.addr
= Data(48, "addr_i") # addr/addr-ok
86 self
.addr_ok_o
= Signal(reset_less
=True) # addr is valid (TLB, L1 etc.)
87 self
.addr_exc_o
= Signal(reset_less
=True) # TODO, "type" of exception
90 self
.ld
= Data(64, "ld_data_o") # ok to be set by L0 Cache/Buf
91 self
.st
= Data(64, "st_data_i") # ok to be set by CompUnit
94 class L0CacheBuffer(Elaboratable
):
97 Note that the final version will have *two* interfaces per LDSTCompUnit,
98 to cover mis-aligned requests. This version is to be used for test
99 purposes (and actively maintained for test purposes)
103 def __init__(self
, n_units
, mem
):
104 self
.n_units
= n_units
107 for i
in range(n_units
):
108 ul
.append(PortInterface("ldst_port%d" % i
))
109 self
.ports
= Array(ul
)
111 def elaborate(self
, platform
):
113 comb
, sync
= m
.d
.comb
, m
.d
.sync
115 # find one LD (or ST) and do it. only one per cycle.
116 # TODO: in the "live" (production) L0Cache/Buffer, merge multiple
117 # LD/STs using mask-expansion - see LenExpand class
119 m
.submodules
.ldpick
= ldpick
= PriorityPicker()
120 m
.submodules
.stpick
= stpick
= PriorityPicker()
122 lds
= Signal(self
.n_units
, reset_less
=True)
123 sts
= Signal(self
.n_units
, reset_less
=True)
126 for i
in range(self
.n_units
):
127 ldi
.append(self
.ports
[i
].is_ld_i
) # accumulate ld-req signals
128 sti
.append(self
.ports
[i
].is_st_i
) # accumulate st-req signals
129 # put the requests into the priority-pickers
130 comb
+= ldpick
.i
.eq(Cat(*ldi
))
131 comb
+= stpick
.i
.eq(Cat(*sti
))
133 # Priority-Pickers pick one and only one request
134 with m
.If(ldpick
.en_o
):
135 rdport
= self
.mem
.rdport
136 ldd_r
= Signal(self
.rwid
, reset_less
=True) # Dest register
138 latchregister(m
, rdport
.data
, ldd_r
, ldlatch
, "ldo_r")
139 sync
+= ldlatch
.eq(self
.load_mem_o
)
140 with m
.If(self
.load_mem_o
):
141 comb
+= rdport
.addr
.eq(self
.addr_o
)
143 with m
.ElIf(stpick
.en_o
):
144 wrport
= self
.mem
.wrport
145 comb
+= wrport
.addr
.eq(self
.addr_o
)
146 comb
+= wrport
.data
.eq(src2_r
)
147 comb
+= wrport
.en
.eq(1)
154 from alu_hier
import ALU
157 dut
= ComputationUnitNoDelay(16, alu
)
158 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
159 with
open("test_compalu.il", "w") as f
:
162 run_simulation(dut
, scoreboard_sim(dut
), vcd_name
='test_compalu.vcd')
165 if __name__
== '__main__':