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, read_latency, write_latency): self.aw = aw self.dw = dw self.nbanks = nbanks 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), ("ack", 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, nmasters, cba_shift): ncontrollers = len(controllers) rca_bits = _getattr_all(controllers, "aw") dw = _getattr_all(controllers, "dw") nbanks = _getattr_all(controllers, "nbanks") read_latency = _getattr_all(controllers, "read_latency") write_latency = _getattr_all(controllers, "write_latency") bank_bits = log2_int(nbanks, False) controller_bits = log2_int(ncontrollers, False) self.masters = [Interface(rca_bits + bank_bits + controller_bits, dw, 1, read_latency, write_latency) for i in range(nmasters)] masters_a = Array(self.masters) ### m_ca, m_ba, m_rca = self._split_master_addresses(controller_bits, bank_bits, rca_bits, cba_shift) for nc, controller in enumerate(controllers): if controller_bits: controller_selected = [ca == nc for ca in m_ca] else: controller_selected = [1]*nmasters for nb in range(nbanks): bank = getattr(controller, "bank"+str(nb)) # arbitrate rr = roundrobin.RoundRobin(nmasters, roundrobin.SP_CE) self.submodules += rr bank_selected = [cs & (ba == nb) for cs, ba in zip(controller_selected, m_ba)] 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.ack) ] # route requests self.comb += [ bank.adr.eq(Array(m_rca)[rr.grant]), bank.we.eq(masters_a[rr.grant].we), bank.stb.eq(masters_a[rr.grant].stb), masters_a[rr.grant].ack.eq(bank.ack) ] # route data writes controller_selected_wl = controller_selected for i in range(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(dw) o_dat_we = Signal(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 controller_bits: for master in self.masters: controller_sel = Signal(controller_bits) for nc, controller in enumerate(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(read_latency): n_controller_sel = Signal(controller_bits) self.sync += n_controller_sel.eq(controller_sel) controller_sel = n_controller_sel self.comb += master.dat_r.eq(Array(controllers)[controller_sel].dat_r) else: self.comb += [master.dat_r.eq(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(controller_bits + bank_bits) rca = Signal(rca_bits) cba_upper = cba_shift + controller_bits + bank_bits self.comb += cba.eq(master.adr[cba_shift:cba_upper]) if cba_shift < rca_bits: self.comb += rca.eq(Cat(master.adr[:cba_shift], master.adr[cba_upper:])) else: self.comb += rca.eq(master.adr[:cba_shift]) if controller_bits: ca = Signal(controller_bits) ba = Signal(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 self.done = False def do_simulation(self, s): s.wr(self.bus.dat_w, 0) s.wr(self.bus.dat_we, 0) if not self.done: if self.transaction is not None and s.rd(self.bus.ack): s.wr(self.bus.stb, 0) if isinstance(self.transaction, TRead): self.transaction_end = s.cycle_counter + self.bus.read_latency else: self.transaction_end = s.cycle_counter + self.bus.write_latency - 1 if self.transaction is None or s.cycle_counter == self.transaction_end: if self.transaction is not None: self.transaction.latency = s.cycle_counter - self.transaction_start - 1 if isinstance(self.transaction, TRead): self.transaction.data = s.rd(self.bus.dat_r) else: s.wr(self.bus.dat_w, self.transaction.data) s.wr(self.bus.dat_we, self.transaction.sel) try: self.transaction = next(self.generator) except StopIteration: self.done = True self.transaction = None if self.transaction is not None: self.transaction_start = s.cycle_counter s.wr(self.bus.stb, 1) s.wr(self.bus.adr, self.transaction.address) if isinstance(self.transaction, TRead): s.wr(self.bus.we, 0) else: s.wr(self.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 Target(Module): def __init__(self, model, *ifargs, **ifkwargs): self.model = model self.bus = Interface(*ifargs, **ifkwargs) self.rd_pipeline = [None]*self.bus.read_latency self.wr_pipeline = [None]*self.bus.write_latency def do_simulation(self, s): # determine banks with pending requests pending_banks = set() for nb in range(self.bus.nbanks): bank = getattr(self.bus, "bank"+str(nb)) if s.rd(bank.stb) and not s.rd(bank.ack): pending_banks.add(nb) # issue new transactions selected_bank_n = self.model.select_bank(pending_banks) for nb in range(self.bus.nbanks): bank = getattr(self.bus, "bank"+str(nb)) if nb == selected_bank_n: s.wr(bank.ack, 1) else: s.wr(bank.ack, 0) rd_transaction = None wr_transaction = None if selected_bank_n >= 0: selected_bank = getattr(self.bus, "bank"+str(selected_bank_n)) if s.rd(selected_bank.we): wr_transaction = selected_bank_n, s.rd(selected_bank.adr) else: rd_transaction = selected_bank_n, s.rd(selected_bank.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: s.wr(self.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], s.rd(self.bus.dat_w), s.rd(self.bus.dat_we))