1 """LOAD / STORE Computation Unit.
3 This module covers POWER9-compliant Load and Store operations,
4 with selection on each between immediate and indexed mode as
5 options for the calculation of the Effective Address (EA),
6 and also "update" mode which optionally stores that EA into
7 an additional register.
10 Note: it took 15 attempts over several weeks to redraw the diagram
11 needed to capture this FSM properly. To understand it fully, please
12 take the time to review the links, video, and diagram.
15 Stores are activated when Go_Store is enabled, and use a sync'd "ADD" to
16 compute the "Effective Address", and, when ready the operand (src3_i)
17 is stored in the computed address (passed through to the PortInterface)
19 Loads are activated when Go_Write[0] is enabled. The EA is computed,
20 and (as long as there was no exception) the data comes out (at any
21 time from the PortInterface), and is captured by the LDCompSTUnit.
23 Both LD and ST may request that the address be computed from summing
24 operand1 (src[0]) with operand2 (src[1]) *or* by summing operand1 with
25 the immediate (from the opcode).
27 Both LD and ST may also request "update" mode (op_is_update) which
28 activates the use of Go_Write[1] to control storage of the EA into
29 a *second* operand in the register file.
31 Thus this module has *TWO* write-requests to the register file and
32 *THREE* read-requests to the register file (not all at the same time!)
33 The regfile port usage is:
45 It's a multi-level Finite State Machine that (unfortunately) nmigen.FSM
46 is not suited to (nmigen.FSM is clock-driven, and some aspects of
47 the nested FSMs below are *combinatorial*).
49 * One FSM covers Operand collection and communication address-side
50 with the LD/ST PortInterface. its role ends when "RD_DONE" is asserted
52 * A second FSM activates to cover LD. it activates if op_is_ld is true
54 * A third FSM activates to cover ST. it activates if op_is_st is true
56 * The "overall" (fourth) FSM coordinates the progression and completion
57 of the three other FSMs, firing "WR_RESET" which switches off "busy"
61 https://libre-soc.org/3d_gpu/ld_st_comp_unit.jpg
63 Links including to walk-through videos:
65 * https://libre-soc.org/3d_gpu/architecture/6600scoreboard/
66 * http://libre-soc.org/openpower/isa/fixedload
67 * http://libre-soc.org/openpower/isa/fixedstore
71 * https://bugs.libre-soc.org/show_bug.cgi?id=302
72 * https://bugs.libre-soc.org/show_bug.cgi?id=216
76 * EA - Effective Address
81 from nmigen
.compat
.sim
import run_simulation
82 from nmigen
.cli
import verilog
, rtlil
83 from nmigen
import Module
, Signal
, Mux
, Cat
, Elaboratable
, Array
, Repl
84 from nmigen
.hdl
.rec
import Record
, Layout
86 from nmutil
.latch
import SRLatch
, latchregister
88 from soc
.experiment
.compalu_multi
import go_record
, CompUnitRecord
89 from soc
.experiment
.l0_cache
import PortInterface
90 from soc
.experiment
.testmem
import TestMemory
92 from soc
.decoder
.power_enums
import InternalOp
, Function
93 from soc
.fu
.ldst
.ldst_input_record
import CompLDSTOpSubset
96 class LDSTCompUnitRecord(CompUnitRecord
):
97 def __init__(self
, rwid
, opsubset
=CompLDSTOpSubset
, name
=None):
98 CompUnitRecord
.__init
__(self
, opsubset
, rwid
,
99 n_src
=3, n_dst
=2, name
=name
)
101 self
.ad
= go_record(1, name
="ad") # address go in, req out
102 self
.st
= go_record(1, name
="st") # store go in, req out
104 self
.addr_exc_o
= Signal(reset_less
=True) # address exception
106 self
.ld_o
= Signal(reset_less
=True) # operation is a LD
107 self
.st_o
= Signal(reset_less
=True) # operation is a ST
109 # hmm... are these necessary?
110 self
.load_mem_o
= Signal(reset_less
=True) # activate memory LOAD
111 self
.stwd_mem_o
= Signal(reset_less
=True) # activate memory STORE
114 class LDSTCompUnit(Elaboratable
):
115 """LOAD / STORE Computation Unit
120 * :pi: a PortInterface to the memory subsystem (read-write capable)
121 * :rwid: register width
122 * :awid: address width
126 * :src_i: Source Operands (RA/RB/RC) - managed by rd[0-3] go/req
130 * :data_o: Dest out (LD) - managed by wr[0] go/req
131 * :addr_o: Address out (LD or ST) - managed by wr[1] go/req
132 * :addr_exc_o: Address/Data Exception occurred. LD/ST must terminate
134 TODO: make addr_exc_o a data-type rather than a single-bit signal
140 * :oper_i: operation being carried out (POWER9 decode LD/ST subset)
141 * :issue_i: LD/ST is being "issued".
142 * :shadown_i: Inverted-shadow is being held (stops STORE *and* WRITE)
143 * :go_rd_i: read is being actioned (latches in src regs)
144 * :go_wr_i: write mode (exactly like ALU CompUnit)
145 * :go_ad_i: address is being actioned (triggers actual mem LD)
146 * :go_st_i: store is being actioned (triggers actual mem STORE)
147 * :go_die_i: resets the unit back to "wait for issue"
149 Control Signals (Out)
150 ---------------------
152 * :busy_o: function unit is busy
153 * :rd_rel_o: request src1/src2
154 * :adr_rel_o: request address (from mem)
155 * :sto_rel_o: request store (to mem)
156 * :req_rel_o: request write (result)
157 * :load_mem_o: activate memory LOAD
158 * :stwd_mem_o: activate memory STORE
160 Note: load_mem_o, stwd_mem_o and req_rel_o MUST all be acknowledged
161 in a single cycle and the CompUnit set back to doing another op.
162 This means deasserting go_st_i, go_ad_i or go_wr_i as appropriate
163 depending on whether the operation is a ST or LD.
166 def __init__(self
, pi
=None, rwid
=64, awid
=48, opsubset
=CompLDSTOpSubset
,
171 self
.cu
= cu
= LDSTCompUnitRecord(rwid
, opsubset
)
172 self
.debugtest
= debugtest
174 # POWER-compliant LD/ST has index and update: *fixed* number of ports
175 self
.n_src
= n_src
= 3 # RA, RB, RT/RS
176 self
.n_dst
= n_dst
= 2 # RA, RT/RS
178 # set up array of src and dest signals
179 for i
in range(n_src
):
180 j
= i
+ 1 # name numbering to match src1/src2
182 setattr(self
, name
, getattr(cu
, name
))
185 for i
in range(n_dst
):
186 j
= i
+ 1 # name numbering to match dest1/2...
187 name
= "dest%d_o" % j
188 setattr(self
, name
, getattr(cu
, name
))
196 self
.go_rd_i
= self
.rd
.go
# temporary naming
197 self
.go_wr_i
= self
.wr
.go
# temporary naming
198 self
.go_ad_i
= self
.ad
.go
# temp naming: go address in
199 self
.go_st_i
= self
.st
.go
# temp naming: go store in
201 self
.rd_rel_o
= self
.rd
.rel
# temporary naming
202 self
.req_rel_o
= self
.wr
.rel
# temporary naming
203 self
.adr_rel_o
= self
.ad
.rel
# request address (from mem)
204 self
.sto_rel_o
= self
.st
.rel
# request store (to mem)
206 self
.issue_i
= cu
.issue_i
207 self
.shadown_i
= cu
.shadown_i
208 self
.go_die_i
= cu
.go_die_i
210 self
.oper_i
= cu
.oper_i
211 self
.src_i
= cu
._src
_i
214 self
.data_o
= self
.dest
[0] # Dest1 out: RT
215 self
.addr_o
= self
.dest
[1] # Address out (LD or ST) - Update => RA
216 self
.addr_exc_o
= cu
.addr_exc_o
217 self
.done_o
= cu
.done_o
218 self
.busy_o
= cu
.busy_o
223 self
.load_mem_o
= cu
.load_mem_o
224 self
.stwd_mem_o
= cu
.stwd_mem_o
226 # HACK: get data width from dest[0]. this is used across the board
227 # (it really shouldn't be)
228 self
.data_wid
= self
.dest
[0].shape()
230 def elaborate(self
, platform
):
236 issue_i
= self
.issue_i
238 #####################
239 # latches for the FSM.
240 m
.submodules
.opc_l
= opc_l
= SRLatch(sync
=False, name
="opc")
241 m
.submodules
.src_l
= src_l
= SRLatch(False, self
.n_src
, name
="src")
242 m
.submodules
.alu_l
= alu_l
= SRLatch(sync
=False, name
="alu")
243 m
.submodules
.adr_l
= adr_l
= SRLatch(sync
=False, name
="adr")
244 m
.submodules
.lod_l
= lod_l
= SRLatch(sync
=False, name
="lod")
245 m
.submodules
.sto_l
= sto_l
= SRLatch(sync
=False, name
="sto")
246 m
.submodules
.wri_l
= wri_l
= SRLatch(sync
=False, name
="wri")
247 m
.submodules
.upd_l
= upd_l
= SRLatch(sync
=False, name
="upd")
248 m
.submodules
.rst_l
= rst_l
= SRLatch(sync
=False, name
="rst")
254 op_is_ld
= Signal(reset_less
=True)
255 op_is_st
= Signal(reset_less
=True)
257 # ALU/LD data output control
258 alu_valid
= Signal(reset_less
=True) # ALU operands are valid
259 alu_ok
= Signal(reset_less
=True) # ALU out ok (1 clock delay valid)
260 addr_ok
= Signal(reset_less
=True) # addr ok (from PortInterface)
261 ld_ok
= Signal(reset_less
=True) # LD out ok from PortInterface
262 wr_any
= Signal(reset_less
=True) # any write (incl. store)
263 rda_any
= Signal(reset_less
=True) # any read for address ops
264 rd_done
= Signal(reset_less
=True) # all *necessary* operands read
265 wr_reset
= Signal(reset_less
=True) # final reset condition
268 alu_o
= Signal(self
.data_wid
, reset_less
=True)
269 ldd_o
= Signal(self
.data_wid
, reset_less
=True)
271 ##############################
272 # reset conditions for latches
274 # temporaries (also convenient when debugging)
275 reset_o
= Signal(reset_less
=True) # reset opcode
276 reset_w
= Signal(reset_less
=True) # reset write
277 reset_u
= Signal(reset_less
=True) # reset update
278 reset_a
= Signal(reset_less
=True) # reset adr latch
279 reset_i
= Signal(reset_less
=True) # issue|die (use a lot)
280 reset_r
= Signal(self
.n_src
, reset_less
=True) # reset src
281 reset_s
= Signal(reset_less
=True) # reset store
283 comb
+= reset_i
.eq(issue_i | self
.go_die_i
) # various
284 comb
+= reset_o
.eq(wr_reset | self
.go_die_i
) # opcode reset
285 comb
+= reset_w
.eq(self
.wr
.go
[0] | self
.go_die_i
) # write reg 1
286 comb
+= reset_u
.eq(self
.wr
.go
[1] | self
.go_die_i
) # update (reg 2)
287 comb
+= reset_s
.eq(self
.go_st_i | self
.go_die_i
) # store reset
288 comb
+= reset_r
.eq(self
.rd
.go |
Repl(self
.go_die_i
, self
.n_src
))
289 comb
+= reset_a
.eq(self
.go_ad_i | self
.go_die_i
)
291 ##########################
292 # FSM implemented through sequence of latches. approximately this:
294 # - src_l[0] : operands
296 # - alu_l : looks after add of src1/2/imm (EA)
297 # - adr_l : waits for add (EA)
298 # - upd_l : waits for adr and Regfile (port 2)
300 # - lod_l : waits for adr (EA) and for LD Data
301 # - wri_l : waits for LD Data and Regfile (port 1)
302 # - st_l : waits for alu and operand2
303 # - rst_l : waits for all FSM paths to converge.
304 # NOTE: use sync to stop combinatorial loops.
306 # opcode latch - inverted so that busy resets to 0
307 # note this MUST be sync so as to avoid a combinatorial loop
308 # between busy_o and issue_i on the reset latch (rst_l)
309 sync
+= opc_l
.s
.eq(issue_i
) # XXX NOTE: INVERTED FROM book!
310 sync
+= opc_l
.r
.eq(reset_o
) # XXX NOTE: INVERTED FROM book!
313 sync
+= src_l
.s
.eq(Repl(issue_i
, self
.n_src
))
314 sync
+= src_l
.r
.eq(reset_r
)
316 # alu latch. use sync-delay between alu_ok and valid to generate pulse
317 comb
+= alu_l
.s
.eq(reset_i
)
318 comb
+= alu_l
.r
.eq(alu_ok
& ~alu_valid
& ~rda_any
)
321 comb
+= adr_l
.s
.eq(reset_i
)
322 sync
+= adr_l
.r
.eq(reset_a
)
325 comb
+= lod_l
.s
.eq(reset_i
)
326 comb
+= lod_l
.r
.eq(ld_ok
)
329 comb
+= wri_l
.s
.eq(issue_i
)
330 sync
+= wri_l
.r
.eq(reset_w
)
332 # update-mode operand latch (EA written to reg 2)
333 sync
+= upd_l
.s
.eq(reset_i
)
334 sync
+= upd_l
.r
.eq(reset_u
)
337 comb
+= sto_l
.s
.eq(addr_ok
& op_is_st
)
338 comb
+= sto_l
.r
.eq(reset_s
)
341 comb
+= rst_l
.s
.eq(addr_ok
) # start when address is ready
342 comb
+= rst_l
.r
.eq(issue_i
)
344 # create a latch/register for the operand
345 oper_r
= CompLDSTOpSubset(name
="oper_r") # Dest register
346 latchregister(m
, self
.oper_i
, oper_r
, self
.issue_i
, name
="oper_l")
349 ldd_r
= Signal(self
.data_wid
, reset_less
=True) # Dest register
350 latchregister(m
, ldd_o
, ldd_r
, ld_ok
, name
="ldo_r")
352 # and for each input from the incoming src operands
354 for i
in range(self
.n_src
):
356 src_r
= Signal(self
.data_wid
, name
=name
, reset_less
=True)
357 latchregister(m
, self
.src_i
[i
], src_r
, src_l
.q
[i
], name
+ '_l')
360 # and one for the output from the ADD (for the EA)
361 addr_r
= Signal(self
.data_wid
, reset_less
=True) # Effective Address
362 latchregister(m
, alu_o
, addr_r
, alu_l
.q
, "ea_r")
364 # select either zero or src1 if opcode says so
365 op_is_z
= oper_r
.zero_a
366 src1_or_z
= Signal(self
.data_wid
, reset_less
=True)
367 m
.d
.comb
+= src1_or_z
.eq(Mux(op_is_z
, 0, srl
[0]))
369 # select either immediate or src2 if opcode says so
370 op_is_imm
= oper_r
.imm_data
.imm_ok
371 src2_or_imm
= Signal(self
.data_wid
, reset_less
=True)
372 m
.d
.comb
+= src2_or_imm
.eq(Mux(op_is_imm
, oper_r
.imm_data
.imm
, srl
[1]))
374 # now do the ALU addr add: one cycle, and say "ready" (next cycle, too)
375 sync
+= alu_o
.eq(src1_or_z
+ src2_or_imm
) # actual EA
376 sync
+= alu_ok
.eq(alu_valid
) # keep ack in sync with EA
378 # decode bits of operand (latched)
379 comb
+= op_is_st
.eq(oper_r
.insn_type
== InternalOp
.OP_STORE
) # ST
380 comb
+= op_is_ld
.eq(oper_r
.insn_type
== InternalOp
.OP_LOAD
) # LD
381 op_is_update
= oper_r
.update
# UPDATE
382 comb
+= self
.load_mem_o
.eq(op_is_ld
& self
.go_ad_i
)
383 comb
+= self
.stwd_mem_o
.eq(op_is_st
& self
.go_st_i
)
384 comb
+= self
.ld_o
.eq(op_is_ld
)
385 comb
+= self
.st_o
.eq(op_is_st
)
387 ############################
388 # Control Signal calculation
392 comb
+= self
.busy_o
.eq(opc_l
.q
) # | self.pi.busy_o) # busy out
394 # 1st operand read-request only when zero not active
395 comb
+= self
.rd
.rel
[0].eq(src_l
.q
[0] & busy_o
& ~op_is_z
)
397 # 2nd operand only needed when immediate is not active
398 comb
+= self
.rd
.rel
[1].eq(src_l
.q
[1] & busy_o
& ~op_is_imm
)
400 # note when the address-related read "go" signals are active
401 comb
+= rda_any
.eq(self
.rd
.go
[0] | self
.rd
.go
[1])
403 # alu input valid when 1st and 2nd ops done (or imm not active)
404 comb
+= alu_valid
.eq(busy_o
& ~
(self
.rd
.rel
[0] | self
.rd
.rel
[1]))
406 # 3rd operand only needed when operation is a store
407 comb
+= self
.rd
.rel
[2].eq(src_l
.q
[2] & busy_o
& op_is_st
)
409 # all reads done when alu is valid and 3rd operand needed
410 comb
+= rd_done
.eq(alu_valid
& ~self
.rd
.rel
[2])
412 # address release only if addr ready, but Port must be idle
413 comb
+= self
.adr_rel_o
.eq(adr_l
.q
& busy_o
)
415 # store release when st ready *and* all operands read (and no shadow)
416 comb
+= self
.st
.rel
.eq(sto_l
.q
& busy_o
& rd_done
& op_is_st
&
419 # request write of LD result. waits until shadow is dropped.
420 comb
+= self
.wr
.rel
[0].eq(wri_l
.q
& busy_o
& lod_l
.qn
& op_is_ld
&
423 # request write of EA result only in update mode
424 comb
+= self
.wr
.rel
[1].eq(upd_l
.q
& busy_o
& op_is_update
&
427 # provide "done" signal: select req_rel for non-LD/ST, adr_rel for LD/ST
428 comb
+= wr_any
.eq(self
.st
.go | self
.wr
.go
[0] | self
.wr
.go
[1])
429 comb
+= wr_reset
.eq(rst_l
.q
& busy_o
& self
.shadown_i
&
430 ~
(self
.st
.rel | self
.wr
.rel
[0] | self
.wr
.rel
[1]) &
431 (lod_l
.qn | op_is_st
))
432 comb
+= self
.done_o
.eq(wr_reset
)
434 ######################
435 # Data/Address outputs
437 # put the LD-output register directly onto the output bus on a go_write
438 with m
.If(self
.wr
.go
[0]):
439 comb
+= self
.data_o
.eq(ldd_r
)
441 # "update" mode, put address out on 2nd go-write
442 with m
.If(op_is_update
& self
.wr
.go
[1]):
443 comb
+= self
.addr_o
.eq(addr_r
)
445 ###########################
446 # PortInterface connections
449 # connect to LD/ST PortInterface.
450 comb
+= pi
.is_ld_i
.eq(op_is_ld
& busy_o
) # decoded-LD
451 comb
+= pi
.is_st_i
.eq(op_is_st
& busy_o
) # decoded-ST
452 comb
+= pi
.op
.eq(self
.oper_i
) # op details (not all needed)
454 comb
+= pi
.addr
.data
.eq(addr_r
) # EA from adder
455 comb
+= pi
.addr
.ok
.eq(alu_ok
& lod_l
.q
) # "go do address stuff"
456 comb
+= self
.addr_exc_o
.eq(pi
.addr_exc_o
) # exception occurred
457 comb
+= addr_ok
.eq(self
.pi
.addr_ok_o
) # no exc, address fine
458 # ld - ld gets latched in via lod_l
459 comb
+= ldd_o
.eq(pi
.ld
.data
) # ld data goes into ld reg (above)
460 comb
+= ld_ok
.eq(pi
.ld
.ok
) # ld.ok *closes* (freezes) ld data
461 # store - data goes in based on go_st
462 comb
+= pi
.st
.data
.eq(srl
[2]) # 3rd operand latch
463 comb
+= pi
.st
.ok
.eq(self
.st
.go
) # go store signals st data valid
475 yield from self
.oper_i
.ports()
476 yield from self
.src_i
484 yield self
.load_mem_o
485 yield self
.stwd_mem_o
491 def wait_for(sig
, wait
=True, test1st
=False):
493 print("wait for", sig
, v
, wait
, test1st
)
494 if test1st
and bool(v
) == wait
:
499 #print("...wait for", sig, v)
504 def store(dut
, src1
, src2
, src3
, imm
, imm_ok
=True, update
=False):
505 print ("ST", src1
, src2
, src3
, imm
, imm_ok
, update
)
506 yield dut
.oper_i
.insn_type
.eq(InternalOp
.OP_STORE
)
507 yield dut
.src1_i
.eq(src1
)
508 yield dut
.src2_i
.eq(src2
)
509 yield dut
.src3_i
.eq(src3
)
510 yield dut
.oper_i
.imm_data
.imm
.eq(imm
)
511 yield dut
.oper_i
.imm_data
.imm_ok
.eq(imm_ok
)
512 yield dut
.oper_i
.update
.eq(update
)
513 yield dut
.issue_i
.eq(1)
515 yield dut
.issue_i
.eq(0)
518 yield dut
.rd
.go
.eq(0b101)
520 yield dut
.rd
.go
.eq(0b111)
521 yield from wait_for(dut
.rd
.rel
)
522 yield dut
.rd
.go
.eq(0)
524 yield from wait_for(dut
.adr_rel_o
, False, test1st
=True)
525 #yield from wait_for(dut.adr_rel_o)
526 #yield dut.ad.go.eq(1)
528 #yield dut.ad.go.eq(0)
531 yield from wait_for(dut
.wr
.rel
[1])
532 yield dut
.wr
.go
.eq(0b10)
534 addr
= yield dut
.addr_o
536 yield dut
.wr
.go
.eq(0)
540 yield from wait_for(dut
.sto_rel_o
)
541 yield dut
.go_st_i
.eq(1)
543 yield dut
.go_st_i
.eq(0)
544 yield from wait_for(dut
.busy_o
, False)
545 #wait_for(dut.stwd_mem_o)
550 def load(dut
, src1
, src2
, imm
, imm_ok
=True, update
=False, zero_a
=False):
551 print ("LD", src1
, src2
, imm
, imm_ok
, update
)
552 yield dut
.oper_i
.insn_type
.eq(InternalOp
.OP_LOAD
)
553 yield dut
.src1_i
.eq(src1
)
554 yield dut
.src2_i
.eq(src2
)
555 yield dut
.oper_i
.zero_a
.eq(zero_a
)
556 yield dut
.oper_i
.imm_data
.imm
.eq(imm
)
557 yield dut
.oper_i
.imm_data
.imm_ok
.eq(imm_ok
)
558 yield dut
.issue_i
.eq(1)
560 yield dut
.issue_i
.eq(0)
569 yield dut
.rd
.go
.eq(rd
)
570 yield from wait_for(dut
.rd
.rel
)
571 yield dut
.rd
.go
.eq(0)
573 yield from wait_for(dut
.adr_rel_o
, False, test1st
=True)
574 #yield dut.ad.go.eq(1)
576 #yield dut.ad.go.eq(0)
579 yield from wait_for(dut
.wr
.rel
[1])
580 yield dut
.wr
.go
.eq(0b10)
582 addr
= yield dut
.addr_o
584 yield dut
.wr
.go
.eq(0)
588 yield from wait_for(dut
.wr
.rel
[0], test1st
=True)
589 yield dut
.wr
.go
.eq(1)
591 data
= yield dut
.data_o
593 yield dut
.wr
.go
.eq(0)
594 yield from wait_for(dut
.busy_o
)
596 # wait_for(dut.stwd_mem_o)
600 def scoreboard_sim(dut
):
605 # two STs (different addresses)
606 yield from store(dut
, 4, 0, 3, 2) # ST reg4 into addr rfile[reg3]+2
607 yield from store(dut
, 2, 0, 9, 2) # ST reg4 into addr rfile[reg9]+2
609 # two LDs (deliberately LD from the 1st address then 2nd)
610 data
, addr
= yield from load(dut
, 4, 0, 2)
611 assert data
== 0x0003, "returned %x" % data
612 data
, addr
= yield from load(dut
, 2, 0, 2)
613 assert data
== 0x0009, "returned %x" % data
617 yield from store(dut
, 4, 5, 3, 0, imm_ok
=False)
618 data
, addr
= yield from load(dut
, 4, 5, 0, imm_ok
=False)
619 assert data
== 0x0003, "returned %x" % data
621 # update-immediate version
622 addr
= yield from store(dut
, 4, 6, 3, 2, update
=True)
623 assert addr
== 0x0006, "returned %x" % addr
625 # update-indexed version
626 data
, addr
= yield from load(dut
, 4, 5, 0, imm_ok
=False, update
=True)
627 assert data
== 0x0003, "returned %x" % data
628 assert addr
== 0x0009, "returned %x" % addr
630 # immediate *and* zero version
631 data
, addr
= yield from load(dut
, 4, 5, 9, imm_ok
=True, zero_a
=True)
632 assert data
== 0x0003, "returned %x" % data
635 class TestLDSTCompUnit(LDSTCompUnit
):
637 def __init__(self
, rwid
):
638 from soc
.experiment
.l0_cache
import TstL0CacheBuffer
639 self
.l0
= l0
= TstL0CacheBuffer()
640 pi
= l0
.l0
.dports
[0].pi
641 LDSTCompUnit
.__init
__(self
, pi
, rwid
, 4)
643 def elaborate(self
, platform
):
644 m
= LDSTCompUnit
.elaborate(self
, platform
)
645 m
.submodules
.l0
= self
.l0
646 m
.d
.comb
+= self
.ad
.go
.eq(self
.ad
.rel
) # link addr-go direct to rel
650 def test_scoreboard():
652 dut
= TestLDSTCompUnit(16)
653 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
654 with
open("test_ldst_comp.il", "w") as f
:
657 run_simulation(dut
, scoreboard_sim(dut
), vcd_name
='test_ldst_comp.vcd')
660 class TestLDSTCompUnitRegSpec(LDSTCompUnit
):
663 from soc
.experiment
.l0_cache
import TstL0CacheBuffer
664 from soc
.fu
.ldst
.pipe_data
import LDSTPipeSpec
665 regspec
= LDSTPipeSpec
.regspec
666 self
.l0
= l0
= TstL0CacheBuffer()
667 pi
= l0
.l0
.dports
[0].pi
668 LDSTCompUnit
.__init
__(self
, pi
, regspec
, 4)
670 def elaborate(self
, platform
):
671 m
= LDSTCompUnit
.elaborate(self
, platform
)
672 m
.submodules
.l0
= self
.l0
673 m
.d
.comb
+= self
.ad
.go
.eq(self
.ad
.rel
) # link addr-go direct to rel
677 def test_scoreboard_regspec():
679 dut
= TestLDSTCompUnitRegSpec()
680 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
681 with
open("test_ldst_comp.il", "w") as f
:
684 run_simulation(dut
, scoreboard_sim(dut
), vcd_name
='test_ldst_regspec.vcd')
687 if __name__
== '__main__':
688 test_scoreboard_regspec()