2012-02-13 10:29:38 -05:00
|
|
|
from migen.bus import wishbone
|
|
|
|
from migen.fhdl.structure import *
|
|
|
|
from migen.corelogic.fsm import FSM
|
|
|
|
from migen.corelogic.misc import split, displacer, chooser
|
|
|
|
from migen.corelogic.record import Record
|
|
|
|
|
|
|
|
# cachesize (in 32-bit words) is the size of the data store, must be a power of 2
|
|
|
|
class WB2ASMI:
|
|
|
|
def __init__(self, cachesize, asmiport):
|
2012-02-15 10:30:16 -05:00
|
|
|
self.wishbone = wishbone.Interface()
|
2012-02-13 10:29:38 -05:00
|
|
|
self.cachesize = cachesize
|
|
|
|
self.asmiport = asmiport
|
|
|
|
if len(self.asmiport.slots) != 1:
|
|
|
|
raise ValueError("ASMI port must have 1 slot")
|
|
|
|
if self.asmiport.hub.dw <= 32:
|
|
|
|
raise ValueError("ASMI data width must be strictly larger than 32")
|
|
|
|
if (self.asmiport.hub.dw % 32) != 0:
|
|
|
|
raise ValueError("ASMI data width must be a multiple of 32")
|
|
|
|
|
|
|
|
def get_fragment(self):
|
|
|
|
comb = []
|
|
|
|
sync = []
|
|
|
|
|
|
|
|
aaw = self.asmiport.hub.aw
|
|
|
|
adw = self.asmiport.hub.dw
|
|
|
|
|
|
|
|
# Split address:
|
|
|
|
# TAG | LINE NUMBER | LINE OFFSET
|
2012-03-14 07:19:42 -04:00
|
|
|
offsetbits = log2_int(adw//32)
|
2012-02-13 10:29:38 -05:00
|
|
|
addressbits = aaw + offsetbits
|
2012-03-14 07:19:42 -04:00
|
|
|
linebits = log2_int(self.cachesize) - offsetbits
|
2012-05-15 09:18:03 -04:00
|
|
|
tagbits = addressbits - linebits
|
2012-02-15 10:30:16 -05:00
|
|
|
adr_offset, adr_line, adr_tag = split(self.wishbone.adr, offsetbits, linebits, tagbits)
|
2012-02-13 10:29:38 -05:00
|
|
|
|
|
|
|
# Data memory
|
2012-11-26 13:14:59 -05:00
|
|
|
data_mem = Memory(adw, 2**linebits)
|
|
|
|
data_port = data_mem.get_port(write_capable=True, we_granularity=8)
|
2012-02-13 10:29:38 -05:00
|
|
|
|
|
|
|
write_from_asmi = Signal()
|
2012-02-13 10:49:43 -05:00
|
|
|
write_to_asmi = Signal()
|
2012-11-29 15:22:38 -05:00
|
|
|
adr_offset_r = Signal(offsetbits)
|
2012-02-13 10:29:38 -05:00
|
|
|
comb += [
|
2012-11-26 13:14:59 -05:00
|
|
|
data_port.adr.eq(adr_line),
|
2012-02-13 10:29:38 -05:00
|
|
|
If(write_from_asmi,
|
2012-11-26 13:14:59 -05:00
|
|
|
data_port.dat_w.eq(self.asmiport.dat_r),
|
|
|
|
data_port.we.eq(Replicate(1, adw//8))
|
2012-02-13 10:29:38 -05:00
|
|
|
).Else(
|
2012-11-26 13:14:59 -05:00
|
|
|
data_port.dat_w.eq(Replicate(self.wishbone.dat_w, adw//32)),
|
2012-02-15 10:30:16 -05:00
|
|
|
If(self.wishbone.cyc & self.wishbone.stb & self.wishbone.we & self.wishbone.ack,
|
2012-11-26 13:14:59 -05:00
|
|
|
displacer(self.wishbone.sel, adr_offset, data_port.we, 2**offsetbits, reverse=True)
|
2012-02-13 10:29:38 -05:00
|
|
|
)
|
|
|
|
),
|
2012-11-26 13:14:59 -05:00
|
|
|
If(write_to_asmi, self.asmiport.dat_w.eq(data_port.dat_r)),
|
2012-05-15 08:41:54 -04:00
|
|
|
self.asmiport.dat_wm.eq(0),
|
2012-11-26 13:14:59 -05:00
|
|
|
chooser(data_port.dat_r, adr_offset_r, self.wishbone.dat_r, reverse=True)
|
2012-02-13 10:29:38 -05:00
|
|
|
]
|
|
|
|
sync += [
|
|
|
|
adr_offset_r.eq(adr_offset)
|
|
|
|
]
|
|
|
|
|
|
|
|
# Tag memory
|
2012-11-26 13:14:59 -05:00
|
|
|
tag_mem = Memory(tagbits+1, 2**linebits)
|
|
|
|
tag_port = tag_mem.get_port(write_capable=True)
|
|
|
|
|
2012-11-29 15:22:38 -05:00
|
|
|
tag_layout = [("tag", tagbits), ("dirty", 1)]
|
2012-02-13 10:29:38 -05:00
|
|
|
tag_do = Record(tag_layout)
|
|
|
|
tag_di = Record(tag_layout)
|
|
|
|
comb += [
|
2012-11-26 13:14:59 -05:00
|
|
|
Cat(*tag_do.flatten()).eq(tag_port.dat_r),
|
|
|
|
tag_port.dat_w.eq(Cat(*tag_di.flatten()))
|
|
|
|
]
|
|
|
|
|
|
|
|
comb += [
|
|
|
|
tag_port.adr.eq(adr_line),
|
2012-02-13 10:29:38 -05:00
|
|
|
tag_di.tag.eq(adr_tag),
|
|
|
|
self.asmiport.adr.eq(Cat(adr_line, tag_do.tag))
|
|
|
|
]
|
|
|
|
|
|
|
|
# Control FSM
|
2012-02-13 10:49:43 -05:00
|
|
|
write_to_asmi_pre = Signal()
|
|
|
|
sync.append(write_to_asmi.eq(write_to_asmi_pre))
|
|
|
|
|
2012-02-13 10:29:38 -05:00
|
|
|
fsm = FSM("IDLE", "TEST_HIT",
|
|
|
|
"EVICT_ISSUE", "EVICT_WAIT",
|
|
|
|
"REFILL_WRTAG", "REFILL_ISSUE", "REFILL_WAIT", "REFILL_COMPLETE")
|
|
|
|
|
|
|
|
fsm.act(fsm.IDLE,
|
2012-02-15 10:30:16 -05:00
|
|
|
If(self.wishbone.cyc & self.wishbone.stb, fsm.next_state(fsm.TEST_HIT))
|
2012-02-13 10:29:38 -05:00
|
|
|
)
|
|
|
|
fsm.act(fsm.TEST_HIT,
|
|
|
|
If(tag_do.tag == adr_tag,
|
2012-02-15 10:30:16 -05:00
|
|
|
self.wishbone.ack.eq(1),
|
|
|
|
If(self.wishbone.we,
|
2012-02-13 10:29:38 -05:00
|
|
|
tag_di.dirty.eq(1),
|
2012-11-26 13:14:59 -05:00
|
|
|
tag_port.we.eq(1)
|
2012-02-13 10:29:38 -05:00
|
|
|
),
|
|
|
|
fsm.next_state(fsm.IDLE)
|
|
|
|
).Else(
|
|
|
|
If(tag_do.dirty,
|
|
|
|
fsm.next_state(fsm.EVICT_ISSUE)
|
|
|
|
).Else(
|
|
|
|
fsm.next_state(fsm.REFILL_WRTAG)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
fsm.act(fsm.EVICT_ISSUE,
|
|
|
|
self.asmiport.stb.eq(1),
|
|
|
|
self.asmiport.we.eq(1),
|
|
|
|
If(self.asmiport.ack, fsm.next_state(fsm.EVICT_WAIT))
|
|
|
|
)
|
|
|
|
fsm.act(fsm.EVICT_WAIT,
|
|
|
|
# Data is actually sampled by the memory controller in the next state.
|
|
|
|
# But since the data memory has one cycle latency, it gets the data
|
|
|
|
# at the address given during this cycle.
|
2012-02-13 10:49:43 -05:00
|
|
|
If(self.asmiport.get_call_expression(),
|
|
|
|
write_to_asmi_pre.eq(1),
|
|
|
|
fsm.next_state(fsm.REFILL_WRTAG)
|
|
|
|
)
|
2012-02-13 10:29:38 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
fsm.act(fsm.REFILL_WRTAG,
|
|
|
|
# Write the tag first to set the ASMI address
|
2012-11-26 13:14:59 -05:00
|
|
|
tag_port.we.eq(1),
|
2012-02-13 10:29:38 -05:00
|
|
|
fsm.next_state(fsm.REFILL_ISSUE)
|
|
|
|
)
|
|
|
|
fsm.act(fsm.REFILL_ISSUE,
|
|
|
|
self.asmiport.stb.eq(1),
|
|
|
|
If(self.asmiport.ack, fsm.next_state(fsm.REFILL_WAIT))
|
|
|
|
)
|
|
|
|
fsm.act(fsm.REFILL_WAIT,
|
|
|
|
If(self.asmiport.get_call_expression(), fsm.next_state(fsm.REFILL_COMPLETE))
|
|
|
|
)
|
|
|
|
fsm.act(fsm.REFILL_COMPLETE,
|
|
|
|
write_from_asmi.eq(1),
|
|
|
|
fsm.next_state(fsm.TEST_HIT)
|
|
|
|
)
|
|
|
|
|
|
|
|
return Fragment(comb, sync, memories=[data_mem, tag_mem]) \
|
|
|
|
+ fsm.get_fragment()
|