litex/migen/bus/lasmibus.py

310 lines
10 KiB
Python

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