mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
move dfi/lasmibus/wishbone2lasmi to MiSoC sdram
This commit is contained in:
parent
225a2d4704
commit
e82531cdf8
4 changed files with 0 additions and 585 deletions
|
@ -1,38 +0,0 @@
|
||||||
from migen.fhdl.std import *
|
|
||||||
from migen.bus.transactions import *
|
|
||||||
from migen.bus import lasmibus
|
|
||||||
from migen.sim.generic import run_simulation
|
|
||||||
|
|
||||||
def my_generator(n):
|
|
||||||
bank = n % 4
|
|
||||||
for x in range(4):
|
|
||||||
t = TWrite(4*bank+x, 0x1000*bank + 0x100*x)
|
|
||||||
yield t
|
|
||||||
print("{0}: Wrote in {1} cycle(s)".format(n, t.latency))
|
|
||||||
|
|
||||||
for x in range(4):
|
|
||||||
t = TRead(4*bank+x)
|
|
||||||
yield t
|
|
||||||
print("{0}: Read {1:x} in {2} cycle(s)".format(n, t.data, t.latency))
|
|
||||||
assert(t.data == 0x1000*bank + 0x100*x)
|
|
||||||
|
|
||||||
class MyModel(lasmibus.TargetModel):
|
|
||||||
def read(self, bank, address):
|
|
||||||
r = 0x1000*bank + 0x100*address
|
|
||||||
#print("read from bank {0} address {1} -> {2:x}".format(bank, address, r))
|
|
||||||
return r
|
|
||||||
|
|
||||||
def write(self, bank, address, data, we):
|
|
||||||
print("write to bank {0} address {1:x} data {2:x}".format(bank, address, data))
|
|
||||||
assert(data == 0x1000*bank + 0x100*address)
|
|
||||||
|
|
||||||
class TB(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.submodules.controller = lasmibus.Target(MyModel(), aw=4, dw=32, nbanks=4, req_queue_size=4,
|
|
||||||
read_latency=4, write_latency=1)
|
|
||||||
self.submodules.xbar = lasmibus.Crossbar([self.controller.bus], 2)
|
|
||||||
self.initiators = [lasmibus.Initiator(my_generator(n), self.xbar.get_master()) for n in range(4)]
|
|
||||||
self.submodules += self.initiators
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
run_simulation(TB())
|
|
|
@ -1,67 +0,0 @@
|
||||||
from migen.fhdl.std import *
|
|
||||||
from migen.genlib.record import *
|
|
||||||
|
|
||||||
def phase_cmd_description(a, ba):
|
|
||||||
return [
|
|
||||||
("address", a, DIR_M_TO_S),
|
|
||||||
("bank", ba, DIR_M_TO_S),
|
|
||||||
("cas_n", 1, DIR_M_TO_S),
|
|
||||||
("cs_n", 1, DIR_M_TO_S),
|
|
||||||
("ras_n", 1, DIR_M_TO_S),
|
|
||||||
("we_n", 1, DIR_M_TO_S),
|
|
||||||
("cke", 1, DIR_M_TO_S),
|
|
||||||
("odt", 1, DIR_M_TO_S),
|
|
||||||
("reset_n", 1, DIR_M_TO_S)
|
|
||||||
]
|
|
||||||
|
|
||||||
def phase_wrdata_description(d):
|
|
||||||
return [
|
|
||||||
("wrdata", d, DIR_M_TO_S),
|
|
||||||
("wrdata_en", 1, DIR_M_TO_S),
|
|
||||||
("wrdata_mask", d//8, DIR_M_TO_S)
|
|
||||||
]
|
|
||||||
|
|
||||||
def phase_rddata_description(d):
|
|
||||||
return [
|
|
||||||
("rddata_en", 1, DIR_M_TO_S),
|
|
||||||
("rddata", d, DIR_S_TO_M),
|
|
||||||
("rddata_valid", 1, DIR_S_TO_M)
|
|
||||||
]
|
|
||||||
|
|
||||||
def phase_description(a, ba, d):
|
|
||||||
r = phase_cmd_description(a, ba)
|
|
||||||
r += phase_wrdata_description(d)
|
|
||||||
r += phase_rddata_description(d)
|
|
||||||
return r
|
|
||||||
|
|
||||||
class Interface(Record):
|
|
||||||
def __init__(self, a, ba, d, nphases=1):
|
|
||||||
layout = [("p"+str(i), phase_description(a, ba, d)) for i in range(nphases)]
|
|
||||||
Record.__init__(self, layout)
|
|
||||||
self.phases = [getattr(self, "p"+str(i)) for i in range(nphases)]
|
|
||||||
for p in self.phases:
|
|
||||||
p.cas_n.reset = 1
|
|
||||||
p.cs_n.reset = 1
|
|
||||||
p.ras_n.reset = 1
|
|
||||||
p.we_n.reset = 1
|
|
||||||
|
|
||||||
# Returns pairs (DFI-mandated signal name, Migen signal object)
|
|
||||||
def get_standard_names(self, m2s=True, s2m=True):
|
|
||||||
r = []
|
|
||||||
add_suffix = len(self.phases) > 1
|
|
||||||
for n, phase in enumerate(self.phases):
|
|
||||||
for field, size, direction in phase.layout:
|
|
||||||
if (m2s and direction == DIR_M_TO_S) or (s2m and direction == DIR_S_TO_M):
|
|
||||||
if add_suffix:
|
|
||||||
if direction == DIR_M_TO_S:
|
|
||||||
suffix = "_p" + str(n)
|
|
||||||
else:
|
|
||||||
suffix = "_w" + str(n)
|
|
||||||
else:
|
|
||||||
suffix = ""
|
|
||||||
r.append(("dfi_" + field + suffix, getattr(phase, field)))
|
|
||||||
return r
|
|
||||||
|
|
||||||
class Interconnect(Module):
|
|
||||||
def __init__(self, master, slave):
|
|
||||||
self.comb += master.connect(slave)
|
|
|
@ -1,309 +0,0 @@
|
||||||
from migen.fhdl.std import *
|
|
||||||
from migen.bus.transactions import *
|
|
||||||
from migen.genlib import roundrobin
|
|
||||||
from migen.genlib.record import *
|
|
||||||
from migen.genlib.misc import optree
|
|
||||||
|
|
||||||
class Interface(Record):
|
|
||||||
def __init__(self, aw, dw, nbanks, req_queue_size, read_latency, write_latency):
|
|
||||||
self.aw = aw
|
|
||||||
self.dw = dw
|
|
||||||
self.nbanks = nbanks
|
|
||||||
self.req_queue_size = req_queue_size
|
|
||||||
self.read_latency = read_latency
|
|
||||||
self.write_latency = write_latency
|
|
||||||
|
|
||||||
bank_layout = [
|
|
||||||
("adr", aw, DIR_M_TO_S),
|
|
||||||
("we", 1, DIR_M_TO_S),
|
|
||||||
("stb", 1, DIR_M_TO_S),
|
|
||||||
("req_ack", 1, DIR_S_TO_M),
|
|
||||||
("dat_ack", 1, DIR_S_TO_M),
|
|
||||||
("lock", 1, DIR_S_TO_M)
|
|
||||||
]
|
|
||||||
if nbanks > 1:
|
|
||||||
layout = [("bank"+str(i), bank_layout) for i in range(nbanks)]
|
|
||||||
else:
|
|
||||||
layout = bank_layout
|
|
||||||
layout += [
|
|
||||||
("dat_w", dw, DIR_M_TO_S),
|
|
||||||
("dat_we", dw//8, DIR_M_TO_S),
|
|
||||||
("dat_r", dw, DIR_S_TO_M)
|
|
||||||
]
|
|
||||||
Record.__init__(self, layout)
|
|
||||||
|
|
||||||
def _getattr_all(l, attr):
|
|
||||||
it = iter(l)
|
|
||||||
r = getattr(next(it), attr)
|
|
||||||
for e in it:
|
|
||||||
if getattr(e, attr) != r:
|
|
||||||
raise ValueError
|
|
||||||
return r
|
|
||||||
|
|
||||||
class Crossbar(Module):
|
|
||||||
def __init__(self, controllers, cba_shift):
|
|
||||||
self._controllers = controllers
|
|
||||||
self._cba_shift = cba_shift
|
|
||||||
|
|
||||||
self._rca_bits = _getattr_all(controllers, "aw")
|
|
||||||
self._dw = _getattr_all(controllers, "dw")
|
|
||||||
self._nbanks = _getattr_all(controllers, "nbanks")
|
|
||||||
self._req_queue_size = _getattr_all(controllers, "req_queue_size")
|
|
||||||
self._read_latency = _getattr_all(controllers, "read_latency")
|
|
||||||
self._write_latency = _getattr_all(controllers, "write_latency")
|
|
||||||
|
|
||||||
self._bank_bits = log2_int(self._nbanks, False)
|
|
||||||
self._controller_bits = log2_int(len(self._controllers), False)
|
|
||||||
|
|
||||||
self._masters = []
|
|
||||||
|
|
||||||
def get_master(self):
|
|
||||||
if self.finalized:
|
|
||||||
raise FinalizeError
|
|
||||||
lasmi_master = Interface(self._rca_bits + self._bank_bits + self._controller_bits,
|
|
||||||
self._dw, 1, self._req_queue_size, self._read_latency, self._write_latency)
|
|
||||||
self._masters.append(lasmi_master)
|
|
||||||
return lasmi_master
|
|
||||||
|
|
||||||
def do_finalize(self):
|
|
||||||
nmasters = len(self._masters)
|
|
||||||
|
|
||||||
m_ca, m_ba, m_rca = self._split_master_addresses(self._controller_bits,
|
|
||||||
self._bank_bits, self._rca_bits, self._cba_shift)
|
|
||||||
|
|
||||||
for nc, controller in enumerate(self._controllers):
|
|
||||||
if self._controller_bits:
|
|
||||||
controller_selected = [ca == nc for ca in m_ca]
|
|
||||||
else:
|
|
||||||
controller_selected = [1]*nmasters
|
|
||||||
master_req_acks = [0]*nmasters
|
|
||||||
master_dat_acks = [0]*nmasters
|
|
||||||
rrs = [roundrobin.RoundRobin(nmasters, roundrobin.SP_CE) for n in range(self._nbanks)]
|
|
||||||
self.submodules += rrs
|
|
||||||
for nb, rr in enumerate(rrs):
|
|
||||||
bank = getattr(controller, "bank"+str(nb))
|
|
||||||
|
|
||||||
# for each master, determine if another bank locks it
|
|
||||||
master_locked = []
|
|
||||||
for nm, master in enumerate(self._masters):
|
|
||||||
locked = 0
|
|
||||||
for other_nb, other_rr in enumerate(rrs):
|
|
||||||
if other_nb != nb:
|
|
||||||
other_bank = getattr(controller, "bank"+str(other_nb))
|
|
||||||
locked = locked | (other_bank.lock & (other_rr.grant == nm))
|
|
||||||
master_locked.append(locked)
|
|
||||||
|
|
||||||
# arbitrate
|
|
||||||
bank_selected = [cs & (ba == nb) & ~locked for cs, ba, locked in zip(controller_selected, m_ba, master_locked)]
|
|
||||||
bank_requested = [bs & master.stb for bs, master in zip(bank_selected, self._masters)]
|
|
||||||
self.comb += [
|
|
||||||
rr.request.eq(Cat(*bank_requested)),
|
|
||||||
rr.ce.eq(~bank.stb & ~bank.lock)
|
|
||||||
]
|
|
||||||
|
|
||||||
# route requests
|
|
||||||
self.comb += [
|
|
||||||
bank.adr.eq(Array(m_rca)[rr.grant]),
|
|
||||||
bank.we.eq(Array(self._masters)[rr.grant].we),
|
|
||||||
bank.stb.eq(Array(bank_requested)[rr.grant])
|
|
||||||
]
|
|
||||||
master_req_acks = [master_req_ack | ((rr.grant == nm) & bank_selected[nm] & bank.req_ack)
|
|
||||||
for nm, master_req_ack in enumerate(master_req_acks)]
|
|
||||||
master_dat_acks = [master_dat_ack | ((rr.grant == nm) & bank.dat_ack)
|
|
||||||
for nm, master_dat_ack in enumerate(master_dat_acks)]
|
|
||||||
|
|
||||||
self.comb += [master.req_ack.eq(master_req_ack) for master, master_req_ack in zip(self._masters, master_req_acks)]
|
|
||||||
self.comb += [master.dat_ack.eq(master_dat_ack) for master, master_dat_ack in zip(self._masters, master_dat_acks)]
|
|
||||||
|
|
||||||
# route data writes
|
|
||||||
controller_selected_wl = controller_selected
|
|
||||||
for i in range(self._write_latency):
|
|
||||||
n_controller_selected_wl = [Signal() for i in range(nmasters)]
|
|
||||||
self.sync += [n.eq(o) for n, o in zip(n_controller_selected_wl, controller_selected_wl)]
|
|
||||||
controller_selected_wl = n_controller_selected_wl
|
|
||||||
dat_w_maskselect = []
|
|
||||||
dat_we_maskselect = []
|
|
||||||
for master, selected in zip(self._masters, controller_selected_wl):
|
|
||||||
o_dat_w = Signal(self._dw)
|
|
||||||
o_dat_we = Signal(self._dw//8)
|
|
||||||
self.comb += If(selected,
|
|
||||||
o_dat_w.eq(master.dat_w),
|
|
||||||
o_dat_we.eq(master.dat_we)
|
|
||||||
)
|
|
||||||
dat_w_maskselect.append(o_dat_w)
|
|
||||||
dat_we_maskselect.append(o_dat_we)
|
|
||||||
self.comb += [
|
|
||||||
controller.dat_w.eq(optree("|", dat_w_maskselect)),
|
|
||||||
controller.dat_we.eq(optree("|", dat_we_maskselect))
|
|
||||||
]
|
|
||||||
|
|
||||||
# route data reads
|
|
||||||
if self._controller_bits:
|
|
||||||
for master in self._masters:
|
|
||||||
controller_sel = Signal(self._controller_bits)
|
|
||||||
for nc, controller in enumerate(self._controllers):
|
|
||||||
for nb in range(nbanks):
|
|
||||||
bank = getattr(controller, "bank"+str(nb))
|
|
||||||
self.comb += If(bank.stb & bank.ack, controller_sel.eq(nc))
|
|
||||||
for i in range(self._read_latency):
|
|
||||||
n_controller_sel = Signal(self._controller_bits)
|
|
||||||
self.sync += n_controller_sel.eq(controller_sel)
|
|
||||||
controller_sel = n_controller_sel
|
|
||||||
self.comb += master.dat_r.eq(Array(self._controllers)[controller_sel].dat_r)
|
|
||||||
else:
|
|
||||||
self.comb += [master.dat_r.eq(self._controllers[0].dat_r) for master in self._masters]
|
|
||||||
|
|
||||||
def _split_master_addresses(self, controller_bits, bank_bits, rca_bits, cba_shift):
|
|
||||||
m_ca = [] # controller address
|
|
||||||
m_ba = [] # bank address
|
|
||||||
m_rca = [] # row and column address
|
|
||||||
for master in self._masters:
|
|
||||||
cba = Signal(self._controller_bits + self._bank_bits)
|
|
||||||
rca = Signal(self._rca_bits)
|
|
||||||
cba_upper = cba_shift + controller_bits + bank_bits
|
|
||||||
self.comb += cba.eq(master.adr[cba_shift:cba_upper])
|
|
||||||
if cba_shift < self._rca_bits:
|
|
||||||
if cba_shift:
|
|
||||||
self.comb += rca.eq(Cat(master.adr[:cba_shift], master.adr[cba_upper:]))
|
|
||||||
else:
|
|
||||||
self.comb += rca.eq(master.adr[cba_upper:])
|
|
||||||
else:
|
|
||||||
self.comb += rca.eq(master.adr[:cba_shift])
|
|
||||||
|
|
||||||
if self._controller_bits:
|
|
||||||
ca = Signal(self._controller_bits)
|
|
||||||
ba = Signal(self._bank_bits)
|
|
||||||
self.comb += Cat(ba, ca).eq(cba)
|
|
||||||
else:
|
|
||||||
ca = None
|
|
||||||
ba = cba
|
|
||||||
|
|
||||||
m_ca.append(ca)
|
|
||||||
m_ba.append(ba)
|
|
||||||
m_rca.append(rca)
|
|
||||||
return m_ca, m_ba, m_rca
|
|
||||||
|
|
||||||
class Initiator(Module):
|
|
||||||
def __init__(self, generator, bus):
|
|
||||||
self.generator = generator
|
|
||||||
self.bus = bus
|
|
||||||
self.transaction_start = 0
|
|
||||||
self.transaction = None
|
|
||||||
self.transaction_end = None
|
|
||||||
|
|
||||||
def do_simulation(self, selfp):
|
|
||||||
selfp.bus.dat_w = 0
|
|
||||||
selfp.bus.dat_we = 0
|
|
||||||
|
|
||||||
if self.transaction is not None:
|
|
||||||
if selfp.bus.req_ack:
|
|
||||||
selfp.bus.stb = 0
|
|
||||||
if selfp.bus.dat_ack:
|
|
||||||
if isinstance(self.transaction, TRead):
|
|
||||||
self.transaction_end = selfp.simulator.cycle_counter + self.bus.read_latency
|
|
||||||
else:
|
|
||||||
self.transaction_end = selfp.simulator.cycle_counter + self.bus.write_latency - 1
|
|
||||||
|
|
||||||
if self.transaction is None or selfp.simulator.cycle_counter == self.transaction_end:
|
|
||||||
if self.transaction is not None:
|
|
||||||
self.transaction.latency = selfp.simulator.cycle_counter - self.transaction_start - 1
|
|
||||||
if isinstance(self.transaction, TRead):
|
|
||||||
self.transaction.data = selfp.bus.dat_r
|
|
||||||
else:
|
|
||||||
selfp.bus.dat_w = self.transaction.data
|
|
||||||
selfp.bus.dat_we = self.transaction.sel
|
|
||||||
try:
|
|
||||||
self.transaction = next(self.generator)
|
|
||||||
except StopIteration:
|
|
||||||
raise StopSimulation
|
|
||||||
if self.transaction is not None:
|
|
||||||
self.transaction_start = selfp.simulator.cycle_counter
|
|
||||||
selfp.bus.stb = 1
|
|
||||||
selfp.bus.adr = self.transaction.address
|
|
||||||
if isinstance(self.transaction, TRead):
|
|
||||||
selfp.bus.we = 0
|
|
||||||
else:
|
|
||||||
selfp.bus.we = 1
|
|
||||||
|
|
||||||
class TargetModel:
|
|
||||||
def __init__(self):
|
|
||||||
self.last_bank = 0
|
|
||||||
|
|
||||||
def read(self, bank, address):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def write(self, bank, address, data, we):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Round-robin scheduling
|
|
||||||
def select_bank(self, pending_banks):
|
|
||||||
if not pending_banks:
|
|
||||||
return -1
|
|
||||||
self.last_bank += 1
|
|
||||||
if self.last_bank > max(pending_banks):
|
|
||||||
self.last_bank = 0
|
|
||||||
while self.last_bank not in pending_banks:
|
|
||||||
self.last_bank += 1
|
|
||||||
return self.last_bank
|
|
||||||
|
|
||||||
class _ReqFIFO(Module):
|
|
||||||
def __init__(self, req_queue_size, bank):
|
|
||||||
self.req_queue_size = req_queue_size
|
|
||||||
self.bank = bank
|
|
||||||
self.contents = []
|
|
||||||
|
|
||||||
def do_simulation(self, selfp):
|
|
||||||
if len(self.contents) < self.req_queue_size:
|
|
||||||
if selfp.bank.stb:
|
|
||||||
self.contents.append((selfp.bank.we, selfp.bank.adr))
|
|
||||||
selfp.bank.req_ack = 1
|
|
||||||
else:
|
|
||||||
selfp.bank.req_ack = 0
|
|
||||||
selfp.bank.lock = bool(self.contents)
|
|
||||||
do_simulation.passive = True
|
|
||||||
|
|
||||||
class Target(Module):
|
|
||||||
def __init__(self, model, *ifargs, **ifkwargs):
|
|
||||||
self.model = model
|
|
||||||
self.bus = Interface(*ifargs, **ifkwargs)
|
|
||||||
self.req_fifos = [_ReqFIFO(self.bus.req_queue_size, getattr(self.bus, "bank"+str(nb)))
|
|
||||||
for nb in range(self.bus.nbanks)]
|
|
||||||
self.submodules += self.req_fifos
|
|
||||||
self.rd_pipeline = [None]*self.bus.read_latency
|
|
||||||
self.wr_pipeline = [None]*(self.bus.write_latency + 1)
|
|
||||||
|
|
||||||
def do_simulation(self, selfp):
|
|
||||||
# determine banks with pending requests
|
|
||||||
pending_banks = set(nb for nb, rf in enumerate(self.req_fifos) if rf.contents)
|
|
||||||
|
|
||||||
# issue new transactions
|
|
||||||
selected_bank_n = self.model.select_bank(pending_banks)
|
|
||||||
selected_transaction = None
|
|
||||||
for nb in range(self.bus.nbanks):
|
|
||||||
bank = getattr(selfp.bus, "bank"+str(nb))
|
|
||||||
if nb == selected_bank_n:
|
|
||||||
bank.dat_ack = 1
|
|
||||||
selected_transaction = self.req_fifos[nb].contents.pop(0)
|
|
||||||
else:
|
|
||||||
bank.dat_ack = 0
|
|
||||||
|
|
||||||
rd_transaction = None
|
|
||||||
wr_transaction = None
|
|
||||||
if selected_bank_n >= 0:
|
|
||||||
we, adr = selected_transaction
|
|
||||||
if we:
|
|
||||||
wr_transaction = selected_bank_n, adr
|
|
||||||
else:
|
|
||||||
rd_transaction = selected_bank_n, adr
|
|
||||||
|
|
||||||
# data pipeline
|
|
||||||
self.rd_pipeline.append(rd_transaction)
|
|
||||||
self.wr_pipeline.append(wr_transaction)
|
|
||||||
done_rd_transaction = self.rd_pipeline.pop(0)
|
|
||||||
done_wr_transaction = self.wr_pipeline.pop(0)
|
|
||||||
if done_rd_transaction is not None:
|
|
||||||
selfp.bus.dat_r = self.model.read(done_rd_transaction[0], done_rd_transaction[1])
|
|
||||||
if done_wr_transaction is not None:
|
|
||||||
self.model.write(done_wr_transaction[0], done_wr_transaction[1],
|
|
||||||
selfp.bus.dat_w, selfp.bus.dat_we)
|
|
||||||
do_simulation.passive = True
|
|
|
@ -1,171 +0,0 @@
|
||||||
from migen.fhdl.std import *
|
|
||||||
from migen.bus import wishbone
|
|
||||||
from migen.bank.description import *
|
|
||||||
from migen.genlib.fsm import FSM, NextState
|
|
||||||
from migen.genlib.misc import split, displacer, chooser
|
|
||||||
from migen.genlib.record import Record, layout_len
|
|
||||||
|
|
||||||
# cachesize (in 32-bit words) is the size of the data store, must be a power of 2
|
|
||||||
class WB2LASMI(Module, AutoCSR):
|
|
||||||
def __init__(self, cachesize, lasmim):
|
|
||||||
self._cachesize = CSRStatus(8, reset=log2_int(cachesize))
|
|
||||||
self.wishbone = wishbone.Interface()
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
data_width = flen(self.wishbone.dat_r)
|
|
||||||
if lasmim.dw > data_width and (lasmim.dw % data_width) != 0:
|
|
||||||
raise ValueError("LASMI data width must be a multiple of {dw}".format(dw=data_width))
|
|
||||||
if lasmim.dw < data_width and (data_width % lasmim.dw) != 0:
|
|
||||||
raise ValueError("WISHBONE data width must be a multiple of {dw}".format(dw=lasmim.dw))
|
|
||||||
|
|
||||||
# Split address:
|
|
||||||
# TAG | LINE NUMBER | LINE OFFSET
|
|
||||||
offsetbits = log2_int(max(lasmim.dw//data_width, 1))
|
|
||||||
addressbits = lasmim.aw + offsetbits
|
|
||||||
linebits = log2_int(cachesize) - offsetbits
|
|
||||||
tagbits = addressbits - linebits
|
|
||||||
wordbits = log2_int(max(data_width//lasmim.dw, 1))
|
|
||||||
adr_offset, adr_line, adr_tag = split(self.wishbone.adr, offsetbits, linebits, tagbits)
|
|
||||||
word = Signal(wordbits) if wordbits else None
|
|
||||||
|
|
||||||
# Data memory
|
|
||||||
data_mem = Memory(lasmim.dw*2**wordbits, 2**linebits)
|
|
||||||
data_port = data_mem.get_port(write_capable=True, we_granularity=8)
|
|
||||||
self.specials += data_mem, data_port
|
|
||||||
|
|
||||||
write_from_lasmi = Signal()
|
|
||||||
write_to_lasmi = Signal()
|
|
||||||
if adr_offset is None:
|
|
||||||
adr_offset_r = None
|
|
||||||
else:
|
|
||||||
adr_offset_r = Signal(offsetbits)
|
|
||||||
self.sync += adr_offset_r.eq(adr_offset)
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
data_port.adr.eq(adr_line),
|
|
||||||
If(write_from_lasmi,
|
|
||||||
displacer(lasmim.dat_r, word, data_port.dat_w),
|
|
||||||
displacer(Replicate(1, lasmim.dw//8), word, data_port.we)
|
|
||||||
).Else(
|
|
||||||
data_port.dat_w.eq(Replicate(self.wishbone.dat_w, max(lasmim.dw//data_width, 1))),
|
|
||||||
If(self.wishbone.cyc & self.wishbone.stb & self.wishbone.we & self.wishbone.ack,
|
|
||||||
displacer(self.wishbone.sel, adr_offset, data_port.we, 2**offsetbits, reverse=True)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
If(write_to_lasmi,
|
|
||||||
chooser(data_port.dat_r, word, lasmim.dat_w),
|
|
||||||
lasmim.dat_we.eq(2**(lasmim.dw//8)-1)
|
|
||||||
),
|
|
||||||
chooser(data_port.dat_r, adr_offset_r, self.wishbone.dat_r, reverse=True)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Tag memory
|
|
||||||
tag_layout = [("tag", tagbits), ("dirty", 1)]
|
|
||||||
tag_mem = Memory(layout_len(tag_layout), 2**linebits)
|
|
||||||
tag_port = tag_mem.get_port(write_capable=True)
|
|
||||||
self.specials += tag_mem, tag_port
|
|
||||||
tag_do = Record(tag_layout)
|
|
||||||
tag_di = Record(tag_layout)
|
|
||||||
self.comb += [
|
|
||||||
tag_do.raw_bits().eq(tag_port.dat_r),
|
|
||||||
tag_port.dat_w.eq(tag_di.raw_bits())
|
|
||||||
]
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
tag_port.adr.eq(adr_line),
|
|
||||||
tag_di.tag.eq(adr_tag)
|
|
||||||
]
|
|
||||||
if word is not None:
|
|
||||||
self.comb += lasmim.adr.eq(Cat(word, adr_line, tag_do.tag))
|
|
||||||
else:
|
|
||||||
self.comb += lasmim.adr.eq(Cat(adr_line, tag_do.tag))
|
|
||||||
|
|
||||||
# Lasmim word computation, word_clr and word_inc will be simplified
|
|
||||||
# at synthesis when wordbits=0
|
|
||||||
word_clr = Signal()
|
|
||||||
word_inc = Signal()
|
|
||||||
if word is not None:
|
|
||||||
self.sync += \
|
|
||||||
If(word_clr,
|
|
||||||
word.eq(0),
|
|
||||||
).Elif(word_inc,
|
|
||||||
word.eq(word+1)
|
|
||||||
)
|
|
||||||
|
|
||||||
def word_is_last(word):
|
|
||||||
if word is not None:
|
|
||||||
return word == 2**wordbits-1
|
|
||||||
else:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# Control FSM
|
|
||||||
assert(lasmim.write_latency >= 1 and lasmim.read_latency >= 1)
|
|
||||||
fsm = FSM(reset_state="IDLE")
|
|
||||||
self.submodules += fsm
|
|
||||||
|
|
||||||
fsm.delayed_enter("EVICT_DATAD", "EVICT_DATA", lasmim.write_latency-1)
|
|
||||||
fsm.delayed_enter("REFILL_DATAD", "REFILL_DATA", lasmim.read_latency-1)
|
|
||||||
|
|
||||||
fsm.act("IDLE",
|
|
||||||
If(self.wishbone.cyc & self.wishbone.stb, NextState("TEST_HIT"))
|
|
||||||
)
|
|
||||||
fsm.act("TEST_HIT",
|
|
||||||
word_clr.eq(1),
|
|
||||||
If(tag_do.tag == adr_tag,
|
|
||||||
self.wishbone.ack.eq(1),
|
|
||||||
If(self.wishbone.we,
|
|
||||||
tag_di.dirty.eq(1),
|
|
||||||
tag_port.we.eq(1)
|
|
||||||
),
|
|
||||||
NextState("IDLE")
|
|
||||||
).Else(
|
|
||||||
If(tag_do.dirty,
|
|
||||||
NextState("EVICT_REQUEST")
|
|
||||||
).Else(
|
|
||||||
NextState("REFILL_WRTAG")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fsm.act("EVICT_REQUEST",
|
|
||||||
lasmim.stb.eq(1),
|
|
||||||
lasmim.we.eq(1),
|
|
||||||
If(lasmim.req_ack, NextState("EVICT_WAIT_DATA_ACK"))
|
|
||||||
)
|
|
||||||
fsm.act("EVICT_WAIT_DATA_ACK",
|
|
||||||
If(lasmim.dat_ack, NextState("EVICT_DATAD"))
|
|
||||||
)
|
|
||||||
fsm.act("EVICT_DATA",
|
|
||||||
write_to_lasmi.eq(1),
|
|
||||||
word_inc.eq(1),
|
|
||||||
If(word_is_last(word),
|
|
||||||
NextState("REFILL_WRTAG"),
|
|
||||||
).Else(
|
|
||||||
NextState("EVICT_REQUEST")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fsm.act("REFILL_WRTAG",
|
|
||||||
# Write the tag first to set the LASMI address
|
|
||||||
tag_port.we.eq(1),
|
|
||||||
word_clr.eq(1),
|
|
||||||
NextState("REFILL_REQUEST")
|
|
||||||
)
|
|
||||||
fsm.act("REFILL_REQUEST",
|
|
||||||
lasmim.stb.eq(1),
|
|
||||||
If(lasmim.req_ack, NextState("REFILL_WAIT_DATA_ACK"))
|
|
||||||
)
|
|
||||||
fsm.act("REFILL_WAIT_DATA_ACK",
|
|
||||||
If(lasmim.dat_ack, NextState("REFILL_DATAD"))
|
|
||||||
)
|
|
||||||
fsm.act("REFILL_DATA",
|
|
||||||
write_from_lasmi.eq(1),
|
|
||||||
word_inc.eq(1),
|
|
||||||
If(word_is_last(word),
|
|
||||||
NextState("TEST_HIT"),
|
|
||||||
).Else(
|
|
||||||
NextState("REFILL_REQUEST")
|
|
||||||
)
|
|
||||||
)
|
|
Loading…
Reference in a new issue