--- /dev/null
+from migen.util.misc import xdir
+from migen.fhdl.std import *
+from migen.fhdl.tracer import get_obj_var_name
+
+
+class _CSRBase(HUID):
+ def __init__(self, size, name):
+ HUID.__init__(self)
+ self.name = get_obj_var_name(name)
+ if self.name is None:
+ raise ValueError("Cannot extract CSR name from code, need to specify.")
+ self.size = size
+
+
+class CSR(_CSRBase):
+ def __init__(self, size=1, name=None):
+ _CSRBase.__init__(self, size, name)
+ self.re = Signal(name=self.name + "_re")
+ self.r = Signal(self.size, name=self.name + "_r")
+ self.w = Signal(self.size, name=self.name + "_w")
+
+
+class _CompoundCSR(_CSRBase, Module):
+ def __init__(self, size, name):
+ _CSRBase.__init__(self, size, name)
+ self.simple_csrs = []
+
+ def get_simple_csrs(self):
+ if not self.finalized:
+ raise FinalizeError
+ return self.simple_csrs
+
+ def do_finalize(self, busword):
+ raise NotImplementedError
+
+
+class CSRStatus(_CompoundCSR):
+ def __init__(self, size=1, reset=0, name=None):
+ _CompoundCSR.__init__(self, size, name)
+ self.status = Signal(self.size, reset=reset)
+
+ def do_finalize(self, busword):
+ nwords = (self.size + busword - 1)//busword
+ for i in reversed(range(nwords)):
+ nbits = min(self.size - i*busword, busword)
+ sc = CSR(nbits, self.name + str(i) if nwords > 1 else self.name)
+ self.comb += sc.w.eq(self.status[i*busword:i*busword+nbits])
+ self.simple_csrs.append(sc)
+
+
+class CSRStorage(_CompoundCSR):
+ def __init__(self, size=1, reset=0, atomic_write=False, write_from_dev=False, alignment_bits=0, name=None):
+ _CompoundCSR.__init__(self, size, name)
+ self.alignment_bits = alignment_bits
+ self.storage_full = Signal(self.size, reset=reset)
+ self.storage = Signal(self.size - self.alignment_bits, reset=reset >> alignment_bits)
+ self.comb += self.storage.eq(self.storage_full[self.alignment_bits:])
+ self.atomic_write = atomic_write
+ self.re = Signal()
+ if write_from_dev:
+ self.we = Signal()
+ self.dat_w = Signal(self.size - self.alignment_bits)
+ self.sync += If(self.we, self.storage_full.eq(self.dat_w << self.alignment_bits))
+
+ def do_finalize(self, busword):
+ nwords = (self.size + busword - 1)//busword
+ if nwords > 1 and self.atomic_write:
+ backstore = Signal(self.size - busword, name=self.name + "_backstore")
+ for i in reversed(range(nwords)):
+ nbits = min(self.size - i*busword, busword)
+ sc = CSR(nbits, self.name + str(i) if nwords else self.name)
+ self.simple_csrs.append(sc)
+ lo = i*busword
+ hi = lo+nbits
+ # read
+ if lo >= self.alignment_bits:
+ self.comb += sc.w.eq(self.storage_full[lo:hi])
+ elif hi > self.alignment_bits:
+ self.comb += sc.w.eq(Cat(Replicate(0, hi - self.alignment_bits),
+ self.storage_full[self.alignment_bits:hi]))
+ else:
+ self.comb += sc.w.eq(0)
+ # write
+ if nwords > 1 and self.atomic_write:
+ if i:
+ self.sync += If(sc.re, backstore[lo-busword:hi-busword].eq(sc.r))
+ else:
+ self.sync += If(sc.re, self.storage_full.eq(Cat(sc.r, backstore)))
+ else:
+ self.sync += If(sc.re, self.storage_full[lo:hi].eq(sc.r))
+ self.sync += self.re.eq(sc.re)
+
+
+def csrprefix(prefix, csrs, done):
+ for csr in csrs:
+ if csr.huid not in done:
+ csr.name = prefix + csr.name
+ done.add(csr.huid)
+
+
+def memprefix(prefix, memories, done):
+ for memory in memories:
+ if memory.huid not in done:
+ memory.name_override = prefix + memory.name_override
+ done.add(memory.huid)
+
+
+def _make_gatherer(method, cls, prefix_cb):
+ def gatherer(self):
+ try:
+ exclude = self.autocsr_exclude
+ except AttributeError:
+ exclude = {}
+ try:
+ prefixed = self.__prefixed
+ except AttributeError:
+ prefixed = self.__prefixed = set()
+ r = []
+ for k, v in xdir(self, True):
+ if k not in exclude:
+ if isinstance(v, cls):
+ r.append(v)
+ elif hasattr(v, method) and callable(getattr(v, method)):
+ items = getattr(v, method)()
+ prefix_cb(k + "_", items, prefixed)
+ r += items
+ return sorted(r, key=lambda x: x.huid)
+ return gatherer
+
+
+class AutoCSR:
+ get_memories = _make_gatherer("get_memories", Memory, memprefix)
+ get_csrs = _make_gatherer("get_csrs", _CSRBase, csrprefix)
+
+
+class GenericBank(Module):
+ def __init__(self, description, busword):
+ # Turn description into simple CSRs and claim ownership of compound CSR modules
+ self.simple_csrs = []
+ for c in description:
+ if isinstance(c, CSR):
+ self.simple_csrs.append(c)
+ else:
+ c.finalize(busword)
+ self.simple_csrs += c.get_simple_csrs()
+ self.submodules += c
+ self.decode_bits = bits_for(len(self.simple_csrs)-1)
--- /dev/null
+from migen.fhdl.std import *
+from migen.bus.transactions import *
+from migen.bank.description import CSRStorage
+from migen.genlib.record import *
+from migen.genlib.misc import chooser
+
+from misoc.interconnect import csr
+
+
+_layout = [
+ ("adr", "address_width", DIR_M_TO_S),
+ ("we", 1, DIR_M_TO_S),
+ ("dat_w", "data_width", DIR_M_TO_S),
+ ("dat_r", "data_width", DIR_S_TO_M)
+]
+
+
+class Interface(Record):
+ def __init__(self, data_width=8, address_width=14):
+ Record.__init__(self, set_layout_parameters(_layout,
+ data_width=data_width, address_width=address_width))
+
+
+class Interconnect(Module):
+ def __init__(self, master, slaves):
+ self.comb += master.connect(*slaves)
+
+
+class Initiator(Module):
+ def __init__(self, generator, bus=None):
+ self.generator = generator
+ if bus is None:
+ bus = Interface()
+ self.bus = bus
+ self.transaction = None
+ self.read_data_ready = False
+ self.done = False
+
+ def do_simulation(self, selfp):
+ if not self.done:
+ if self.transaction is not None:
+ if isinstance(self.transaction, TRead):
+ if self.read_data_ready:
+ self.transaction.data = selfp.bus.dat_r
+ self.transaction = None
+ self.read_data_ready = False
+ else:
+ self.read_data_ready = True
+ else:
+ selfp.bus.we = 0
+ self.transaction = None
+ if self.transaction is None:
+ try:
+ self.transaction = next(self.generator)
+ except StopIteration:
+ self.transaction = None
+ raise StopSimulation
+ if self.transaction is not None:
+ selfp.bus.adr = self.transaction.address
+ if isinstance(self.transaction, TWrite):
+ selfp.bus.we = 1
+ selfp.bus.dat_w = self.transaction.data
+
+
+class SRAM(Module):
+ def __init__(self, mem_or_size, address, read_only=None, init=None, bus=None):
+ if bus is None:
+ bus = Interface()
+ self.bus = bus
+ data_width = flen(self.bus.dat_w)
+ if isinstance(mem_or_size, Memory):
+ mem = mem_or_size
+ else:
+ mem = Memory(data_width, mem_or_size//(data_width//8), init=init)
+ csrw_per_memw = (mem.width + data_width - 1)//data_width
+ word_bits = log2_int(csrw_per_memw)
+ page_bits = log2_int((mem.depth*csrw_per_memw + 511)//512, False)
+ if page_bits:
+ self._page = CSRStorage(page_bits, name=mem.name_override + "_page")
+ else:
+ self._page = None
+ if read_only is None:
+ if hasattr(mem, "bus_read_only"):
+ read_only = mem.bus_read_only
+ else:
+ read_only = False
+
+ ###
+
+ port = mem.get_port(write_capable=not read_only)
+ self.specials += mem, port
+
+ sel = Signal()
+ sel_r = Signal()
+ self.sync += sel_r.eq(sel)
+ self.comb += sel.eq(self.bus.adr[9:] == address)
+
+ if word_bits:
+ word_index = Signal(word_bits)
+ word_expanded = Signal(csrw_per_memw*data_width)
+ self.sync += word_index.eq(self.bus.adr[:word_bits])
+ self.comb += [
+ word_expanded.eq(port.dat_r),
+ If(sel_r,
+ chooser(word_expanded, word_index, self.bus.dat_r, n=csrw_per_memw, reverse=True)
+ )
+ ]
+ if not read_only:
+ wregs = []
+ for i in range(csrw_per_memw-1):
+ wreg = Signal(data_width)
+ self.sync += If(sel & self.bus.we & (self.bus.adr[:word_bits] == i), wreg.eq(self.bus.dat_w))
+ wregs.append(wreg)
+ memword_chunks = [self.bus.dat_w] + list(reversed(wregs))
+ self.comb += [
+ port.we.eq(sel & self.bus.we & (self.bus.adr[:word_bits] == csrw_per_memw - 1)),
+ port.dat_w.eq(Cat(*memword_chunks))
+ ]
+ else:
+ self.comb += If(sel_r, self.bus.dat_r.eq(port.dat_r))
+ if not read_only:
+ self.comb += [
+ port.we.eq(sel & self.bus.we),
+ port.dat_w.eq(self.bus.dat_w)
+ ]
+
+ if self._page is None:
+ self.comb += port.adr.eq(self.bus.adr[word_bits:word_bits+flen(port.adr)])
+ else:
+ pv = self._page.storage
+ self.comb += port.adr.eq(Cat(self.bus.adr[word_bits:word_bits+flen(port.adr)-flen(pv)], pv))
+
+ def get_csrs(self):
+ if self._page is None:
+ return []
+ else:
+ return [self._page]
+
+
+class CSRBank(csr.GenericBank):
+ def __init__(self, description, address=0, bus=None):
+ if bus is None:
+ bus = Interface()
+ self.bus = bus
+
+ ###
+
+ GenericBank.__init__(self, description, flen(self.bus.dat_w))
+
+ sel = Signal()
+ self.comb += sel.eq(self.bus.adr[9:] == address)
+
+ for i, c in enumerate(self.simple_csrs):
+ self.comb += [
+ c.r.eq(self.bus.dat_w[:c.size]),
+ c.re.eq(sel & \
+ self.bus.we & \
+ (self.bus.adr[:self.decode_bits] == i))
+ ]
+
+ brcases = dict((i, self.bus.dat_r.eq(c.w)) for i, c in enumerate(self.simple_csrs))
+ self.sync += [
+ self.bus.dat_r.eq(0),
+ If(sel, Case(self.bus.adr[:self.decode_bits], brcases))
+ ]
+
+
+# address_map(name, memory) returns the CSR offset at which to map
+# the CSR object (register bank or memory).
+# If memory=None, the object is the register bank of object source.name.
+# Otherwise, it is a memory object belonging to source.name.
+# address_map is called exactly once for each object at each call to
+# scan(), so it can have side effects.
+class CSRBankArray(Module):
+ def __init__(self, source, address_map, *ifargs, **ifkwargs):
+ self.source = source
+ self.address_map = address_map
+ self.scan(ifargs, ifkwargs)
+
+ def scan(self, ifargs, ifkwargs):
+ self.banks = []
+ self.srams = []
+ for name, obj in xdir(self.source, True):
+ if hasattr(obj, "get_csrs"):
+ csrs = obj.get_csrs()
+ else:
+ csrs = []
+ if hasattr(obj, "get_memories"):
+ memories = obj.get_memories()
+ for memory in memories:
+ mapaddr = self.address_map(name, memory)
+ if mapaddr is None:
+ continue
+ sram_bus = csr.Interface(*ifargs, **ifkwargs)
+ mmap = csr.SRAM(memory, mapaddr, bus=sram_bus)
+ self.submodules += mmap
+ csrs += mmap.get_csrs()
+ self.srams.append((name, memory, mapaddr, mmap))
+ if csrs:
+ mapaddr = self.address_map(name, None)
+ if mapaddr is None:
+ continue
+ bank_bus = csr.Interface(*ifargs, **ifkwargs)
+ rmap = Bank(csrs, mapaddr, bus=bank_bus)
+ self.submodules += rmap
+ self.banks.append((name, csrs, mapaddr, rmap))
+
+ def get_rmaps(self):
+ return [rmap for name, csrs, mapaddr, rmap in self.banks]
+
+ def get_mmaps(self):
+ return [mmap for name, memory, mapaddr, mmap in self.srams]
+
+ def get_buses(self):
+ return [i.bus for i in self.get_rmaps() + self.get_mmaps()]
--- /dev/null
+from migen.util.misc import xdir
+from migen.fhdl.std import *
+from migen.bank.description import *
+from migen.genlib.misc import optree
+
+
+class _EventSource(HUID):
+ def __init__(self):
+ HUID.__init__(self)
+ self.status = Signal() # value in the status register
+ self.pending = Signal() # value in the pending register + assert irq if unmasked
+ self.trigger = Signal() # trigger signal interface to the user design
+ self.clear = Signal() # clearing attempt by W1C to pending register, ignored by some event sources
+
+
+# set on a positive trigger pulse
+class EventSourcePulse(Module, _EventSource):
+ def __init__(self):
+ _EventSource.__init__(self)
+ self.comb += self.status.eq(0)
+ self.sync += [
+ If(self.clear, self.pending.eq(0)),
+ If(self.trigger, self.pending.eq(1))
+ ]
+
+
+# set on the falling edge of the trigger, status = trigger
+class EventSourceProcess(Module, _EventSource):
+ def __init__(self):
+ _EventSource.__init__(self)
+ self.comb += self.status.eq(self.trigger)
+ old_trigger = Signal()
+ self.sync += [
+ If(self.clear, self.pending.eq(0)),
+ old_trigger.eq(self.trigger),
+ If(~self.trigger & old_trigger, self.pending.eq(1))
+ ]
+
+
+# all status set by external trigger
+class EventSourceLevel(Module, _EventSource):
+ def __init__(self):
+ _EventSource.__init__(self)
+ self.comb += [
+ self.status.eq(self.trigger),
+ self.pending.eq(self.trigger)
+ ]
+
+
+class EventManager(Module, AutoCSR):
+ def __init__(self):
+ self.irq = Signal()
+
+ def do_finalize(self):
+ sources_u = [v for k, v in xdir(self, True) if isinstance(v, _EventSource)]
+ sources = sorted(sources_u, key=lambda x: x.huid)
+ n = len(sources)
+ self.status = CSR(n)
+ self.pending = CSR(n)
+ self.enable = CSRStorage(n)
+
+ for i, source in enumerate(sources):
+ self.comb += [
+ self.status.w[i].eq(source.status),
+ If(self.pending.re & self.pending.r[i], source.clear.eq(1)),
+ self.pending.w[i].eq(source.pending)
+ ]
+
+ irqs = [self.pending.w[i] & self.enable.storage[i] for i in range(n)]
+ self.comb += self.irq.eq(optree("|", irqs))
+
+ def __setattr__(self, name, value):
+ object.__setattr__(self, name, value)
+ if isinstance(value, _EventSource):
+ if self.finalized:
+ raise FinalizeError
+ self.submodules += value
+
+
+class SharedIRQ(Module):
+ def __init__(self, *event_managers):
+ self.irq = Signal()
+ self.comb += self.irq.eq(optree("|", [ev.irq for ev in event_managers]))
--- /dev/null
+from migen 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_w_ack", 1, DIR_S_TO_M),
+ ("dat_r_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)
+
+
+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
--- /dev/null
+from migen import *
+from migen.genlib import roundrobin
+from migen.genlib.record import *
+from migen.genlib.misc import optree
+
+from misoc.mem.sdram.core.lasmibus import Interface
+
+
+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 LASMIxbar(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_w_acks = [0]*nmasters
+ master_dat_r_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_w_acks = [master_dat_w_ack | ((rr.grant == nm) & bank.dat_w_ack)
+ for nm, master_dat_w_ack in enumerate(master_dat_w_acks)]
+ master_dat_r_acks = [master_dat_r_ack | ((rr.grant == nm) & bank.dat_r_ack)
+ for nm, master_dat_r_ack in enumerate(master_dat_r_acks)]
+
+ for nm, master_dat_w_ack in enumerate(master_dat_w_acks):
+ for i in range(self._write_latency):
+ new_master_dat_w_ack = Signal()
+ self.sync += new_master_dat_w_ack.eq(master_dat_w_ack)
+ master_dat_w_ack = new_master_dat_w_ack
+ master_dat_w_acks[nm] = master_dat_w_ack
+
+ for nm, master_dat_r_ack in enumerate(master_dat_r_acks):
+ for i in range(self._read_latency):
+ new_master_dat_r_ack = Signal()
+ self.sync += new_master_dat_r_ack.eq(master_dat_r_ack)
+ master_dat_r_ack = new_master_dat_r_ack
+ master_dat_r_acks[nm] = master_dat_r_ack
+
+ 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_w_ack.eq(master_dat_w_ack) for master, master_dat_w_ack in zip(self._masters, master_dat_w_acks)]
+ self.comb += [master.dat_r_ack.eq(master_dat_r_ack) for master, master_dat_r_ack in zip(self._masters, master_dat_r_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
+++ /dev/null
-from migen 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_w_ack", 1, DIR_S_TO_M),
- ("dat_r_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)
-
-
-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
+++ /dev/null
-from migen import *
-from migen.genlib import roundrobin
-from migen.genlib.record import *
-from migen.genlib.misc import optree
-
-from misoc.mem.sdram.core.lasmibus import Interface
-
-
-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 LASMIxbar(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_w_acks = [0]*nmasters
- master_dat_r_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_w_acks = [master_dat_w_ack | ((rr.grant == nm) & bank.dat_w_ack)
- for nm, master_dat_w_ack in enumerate(master_dat_w_acks)]
- master_dat_r_acks = [master_dat_r_ack | ((rr.grant == nm) & bank.dat_r_ack)
- for nm, master_dat_r_ack in enumerate(master_dat_r_acks)]
-
- for nm, master_dat_w_ack in enumerate(master_dat_w_acks):
- for i in range(self._write_latency):
- new_master_dat_w_ack = Signal()
- self.sync += new_master_dat_w_ack.eq(master_dat_w_ack)
- master_dat_w_ack = new_master_dat_w_ack
- master_dat_w_acks[nm] = master_dat_w_ack
-
- for nm, master_dat_r_ack in enumerate(master_dat_r_acks):
- for i in range(self._read_latency):
- new_master_dat_r_ack = Signal()
- self.sync += new_master_dat_r_ack.eq(master_dat_r_ack)
- master_dat_r_ack = new_master_dat_r_ack
- master_dat_r_acks[nm] = master_dat_r_ack
-
- 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_w_ack.eq(master_dat_w_ack) for master, master_dat_w_ack in zip(self._masters, master_dat_w_acks)]
- self.comb += [master.dat_r_ack.eq(master_dat_r_ack) for master, master_dat_r_ack in zip(self._masters, master_dat_r_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
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib import roundrobin
+from migen.genlib.record import *
+from migen.genlib.misc import split, displacer, optree, chooser
+from migen.genlib.misc import FlipFlop, Counter
+from migen.genlib.fsm import FSM, NextState
+from migen.bus.transactions import *
+
+from misoc.interconnect import csr
+
+
+_layout = [
+ ("adr", 30, DIR_M_TO_S),
+ ("dat_w", "data_width", DIR_M_TO_S),
+ ("dat_r", "data_width", DIR_S_TO_M),
+ ("sel", "sel_width", DIR_M_TO_S),
+ ("cyc", 1, DIR_M_TO_S),
+ ("stb", 1, DIR_M_TO_S),
+ ("ack", 1, DIR_S_TO_M),
+ ("we", 1, DIR_M_TO_S),
+ ("cti", 3, DIR_M_TO_S),
+ ("bte", 2, DIR_M_TO_S),
+ ("err", 1, DIR_S_TO_M)
+]
+
+
+class Interface(Record):
+ def __init__(self, data_width=32):
+ Record.__init__(self, set_layout_parameters(_layout,
+ data_width=data_width,
+ sel_width=data_width//8))
+
+
+class InterconnectPointToPoint(Module):
+ def __init__(self, master, slave):
+ self.comb += master.connect(slave)
+
+
+class Arbiter(Module):
+ def __init__(self, masters, target):
+ self.submodules.rr = roundrobin.RoundRobin(len(masters))
+
+ # mux master->slave signals
+ for name, size, direction in _layout:
+ if direction == DIR_M_TO_S:
+ choices = Array(getattr(m, name) for m in masters)
+ self.comb += getattr(target, name).eq(choices[self.rr.grant])
+
+ # connect slave->master signals
+ for name, size, direction in _layout:
+ if direction == DIR_S_TO_M:
+ source = getattr(target, name)
+ for i, m in enumerate(masters):
+ dest = getattr(m, name)
+ if name == "ack" or name == "err":
+ self.comb += dest.eq(source & (self.rr.grant == i))
+ else:
+ self.comb += dest.eq(source)
+
+ # connect bus requests to round-robin selector
+ reqs = [m.cyc for m in masters]
+ self.comb += self.rr.request.eq(Cat(*reqs))
+
+
+class Decoder(Module):
+ # slaves is a list of pairs:
+ # 0) function that takes the address signal and returns a FHDL expression
+ # that evaluates to 1 when the slave is selected and 0 otherwise.
+ # 1) wishbone.Slave reference.
+ # register adds flip-flops after the address comparators. Improves timing,
+ # but breaks Wishbone combinatorial feedback.
+ def __init__(self, master, slaves, register=False):
+ ns = len(slaves)
+ slave_sel = Signal(ns)
+ slave_sel_r = Signal(ns)
+
+ # decode slave addresses
+ self.comb += [slave_sel[i].eq(fun(master.adr))
+ for i, (fun, bus) in enumerate(slaves)]
+ if register:
+ self.sync += slave_sel_r.eq(slave_sel)
+ else:
+ self.comb += slave_sel_r.eq(slave_sel)
+
+ # connect master->slaves signals except cyc
+ for slave in slaves:
+ for name, size, direction in _layout:
+ if direction == DIR_M_TO_S and name != "cyc":
+ self.comb += getattr(slave[1], name).eq(getattr(master, name))
+
+ # combine cyc with slave selection signals
+ self.comb += [slave[1].cyc.eq(master.cyc & slave_sel[i])
+ for i, slave in enumerate(slaves)]
+
+ # generate master ack (resp. err) by ORing all slave acks (resp. errs)
+ self.comb += [
+ master.ack.eq(optree("|", [slave[1].ack for slave in slaves])),
+ master.err.eq(optree("|", [slave[1].err for slave in slaves]))
+ ]
+
+ # mux (1-hot) slave data return
+ masked = [Replicate(slave_sel_r[i], flen(master.dat_r)) & slaves[i][1].dat_r for i in range(ns)]
+ self.comb += master.dat_r.eq(optree("|", masked))
+
+
+class InterconnectShared(Module):
+ def __init__(self, masters, slaves, register=False):
+ shared = Interface()
+ self.submodules += Arbiter(masters, shared)
+ self.submodules += Decoder(shared, slaves, register)
+
+
+class Crossbar(Module):
+ def __init__(self, masters, slaves, register=False):
+ matches, busses = zip(*slaves)
+ access = [[Interface() for j in slaves] for i in masters]
+ # decode each master into its access row
+ for row, master in zip(access, masters):
+ row = list(zip(matches, row))
+ self.submodules += Decoder(master, row, register)
+ # arbitrate each access column onto its slave
+ for column, bus in zip(zip(*access), busses):
+ self.submodules += Arbiter(column, bus)
+
+
+class DownConverter(Module):
+ """DownConverter
+
+ This module splits Wishbone accesses from a master interface to a smaller
+ slave interface.
+
+ Writes:
+ Writes from master are splitted N writes to the slave. Access is acked when the last
+ access is acked by the slave.
+
+ Reads:
+ Read from master are splitted in N reads to the the slave. Read datas from
+ the slave are cached before being presented concatenated on the last access.
+
+ TODO:
+ Manage err signal? (Not implemented since we generally don't use it on Migen/MiSoC modules)
+ """
+ def __init__(self, master, slave):
+ dw_from = flen(master.dat_r)
+ dw_to = flen(slave.dat_w)
+ ratio = dw_from//dw_to
+
+ # # #
+
+ read = Signal()
+ write = Signal()
+
+ counter = Counter(max=ratio)
+ self.submodules += counter
+ counter_done = Signal()
+ self.comb += counter_done.eq(counter.value == ratio-1)
+
+ # Main FSM
+ self.submodules.fsm = fsm = FSM(reset_state="IDLE")
+ fsm.act("IDLE",
+ counter.reset.eq(1),
+ If(master.stb & master.cyc,
+ If(master.we,
+ NextState("WRITE")
+ ).Else(
+ NextState("READ")
+ )
+ )
+ )
+ fsm.act("WRITE",
+ write.eq(1),
+ slave.we.eq(1),
+ slave.cyc.eq(1),
+ If(master.stb & master.cyc,
+ slave.stb.eq(1),
+ If(slave.ack,
+ counter.ce.eq(1),
+ If(counter_done,
+ master.ack.eq(1),
+ NextState("IDLE")
+ )
+ )
+ ).Elif(~master.cyc,
+ NextState("IDLE")
+ )
+ )
+ fsm.act("READ",
+ read.eq(1),
+ slave.cyc.eq(1),
+ If(master.stb & master.cyc,
+ slave.stb.eq(1),
+ If(slave.ack,
+ counter.ce.eq(1),
+ If(counter_done,
+ master.ack.eq(1),
+ NextState("IDLE")
+ )
+ )
+ ).Elif(~master.cyc,
+ NextState("IDLE")
+ )
+ )
+
+ # Address
+ self.comb += [
+ If(counter_done,
+ slave.cti.eq(7) # indicate end of burst
+ ).Else(
+ slave.cti.eq(2)
+ ),
+ slave.adr.eq(Cat(counter.value, master.adr))
+ ]
+
+ # Datapath
+ cases = {}
+ for i in range(ratio):
+ cases[i] = [
+ slave.sel.eq(master.sel[i*dw_to//8:(i+1)*dw_to]),
+ slave.dat_w.eq(master.dat_w[i*dw_to:(i+1)*dw_to])
+ ]
+ self.comb += Case(counter.value, cases)
+
+
+ cached_data = Signal(dw_from)
+ self.comb += master.dat_r.eq(Cat(cached_data[dw_to:], slave.dat_r))
+ self.sync += \
+ If(read & counter.ce,
+ cached_data.eq(master.dat_r)
+ )
+
+
+class UpConverter(Module):
+ """UpConverter
+
+ This module up-converts wishbone accesses and bursts from a master interface
+ to a wider slave interface. This allows efficient use wishbone bursts.
+
+ Writes:
+ Wishbone writes are cached before being written to the slave. Access to
+ the slave is done at the end of a burst or when address reach end of burst
+ addressing.
+
+ Reads:
+ Cache is refilled only at the beginning of each burst, the subsequent
+ reads of a burst use the cached data.
+
+ TODO:
+ Manage err signal? (Not implemented since we generally don't use it on Migen/MiSoC modules)
+ """
+ def __init__(self, master, slave):
+ dw_from = flen(master.dat_r)
+ dw_to = flen(slave.dat_w)
+ ratio = dw_to//dw_from
+ ratiobits = log2_int(ratio)
+
+ # # #
+
+ write = Signal()
+ evict = Signal()
+ refill = Signal()
+ read = Signal()
+
+ address = FlipFlop(30)
+ self.submodules += address
+ self.comb += address.d.eq(master.adr)
+
+ counter = Counter(max=ratio)
+ self.submodules += counter
+ counter_offset = Signal(max=ratio)
+ counter_done = Signal()
+ self.comb += [
+ counter_offset.eq(address.q),
+ counter_done.eq((counter.value + counter_offset) == ratio-1)
+ ]
+
+ cached_data = Signal(dw_to)
+ cached_sel = Signal(dw_to//8)
+
+ end_of_burst = Signal()
+ self.comb += end_of_burst.eq(~master.cyc |
+ (master.stb & master.cyc & master.ack & ((master.cti == 7) | counter_done)))
+
+
+ need_refill = FlipFlop(reset=1)
+ self.submodules += need_refill
+ self.comb += [
+ need_refill.reset.eq(end_of_burst),
+ need_refill.d.eq(0)
+ ]
+
+ # Main FSM
+ self.submodules.fsm = fsm = FSM()
+ fsm.act("IDLE",
+ counter.reset.eq(1),
+ If(master.stb & master.cyc,
+ address.ce.eq(1),
+ If(master.we,
+ NextState("WRITE")
+ ).Else(
+ If(need_refill.q,
+ NextState("REFILL")
+ ).Else(
+ NextState("READ")
+ )
+ )
+ )
+ )
+ fsm.act("WRITE",
+ If(master.stb & master.cyc,
+ write.eq(1),
+ counter.ce.eq(1),
+ master.ack.eq(1),
+ If(counter_done,
+ NextState("EVICT")
+ )
+ ).Elif(~master.cyc,
+ NextState("EVICT")
+ )
+ )
+ fsm.act("EVICT",
+ evict.eq(1),
+ slave.stb.eq(1),
+ slave.we.eq(1),
+ slave.cyc.eq(1),
+ slave.dat_w.eq(cached_data),
+ slave.sel.eq(cached_sel),
+ If(slave.ack,
+ NextState("IDLE")
+ )
+ )
+ fsm.act("REFILL",
+ refill.eq(1),
+ slave.stb.eq(1),
+ slave.cyc.eq(1),
+ If(slave.ack,
+ need_refill.ce.eq(1),
+ NextState("READ")
+ )
+ )
+ fsm.act("READ",
+ read.eq(1),
+ If(master.stb & master.cyc,
+ master.ack.eq(1)
+ ),
+ NextState("IDLE")
+ )
+
+ # Address
+ self.comb += [
+ slave.cti.eq(7), # we are not able to generate bursts since up-converting
+ slave.adr.eq(address.q[ratiobits:])
+ ]
+
+ # Datapath
+ cached_datas = [FlipFlop(dw_from) for i in range(ratio)]
+ cached_sels = [FlipFlop(dw_from//8) for i in range(ratio)]
+ self.submodules += cached_datas, cached_sels
+
+ cases = {}
+ for i in range(ratio):
+ write_sel = Signal()
+ cases[i] = write_sel.eq(1)
+ self.comb += [
+ cached_sels[i].reset.eq(counter.reset),
+ If(write,
+ cached_datas[i].d.eq(master.dat_w),
+ ).Else(
+ cached_datas[i].d.eq(slave.dat_r[dw_from*i:dw_from*(i+1)])
+ ),
+ cached_sels[i].d.eq(master.sel),
+ If((write & write_sel) | refill,
+ cached_datas[i].ce.eq(1),
+ cached_sels[i].ce.eq(1)
+ )
+ ]
+ self.comb += Case(counter.value + counter_offset, cases)
+
+ cases = {}
+ for i in range(ratio):
+ cases[i] = master.dat_r.eq(cached_datas[i].q)
+ self.comb += Case(address.q[:ratiobits], cases)
+
+ self.comb += [
+ cached_data.eq(Cat([cached_data.q for cached_data in cached_datas])),
+ cached_sel.eq(Cat([cached_sel.q for cached_sel in cached_sels]))
+ ]
+
+
+class Converter(Module):
+ """Converter
+
+ This module is a wrapper for DownConverter and UpConverter.
+ It should preferably be used rather than direct instantiations
+ of specific converters.
+ """
+ def __init__(self, master, slave):
+ self.master = master
+ self.slave = slave
+
+ # # #
+
+ dw_from = flen(master.dat_r)
+ dw_to = flen(slave.dat_r)
+ if dw_from > dw_to:
+ downconverter = DownConverter(master, slave)
+ self.submodules += downconverter
+ elif dw_from < dw_to:
+ upconverter = UpConverter(master, slave)
+ self.submodules += upconverter
+ else:
+ Record.connect(master, slave)
+
+
+class Cache(Module):
+ """Cache
+
+ This module is a write-back wishbone cache that can be used as a L2 cache.
+ Cachesize (in 32-bit words) is the size of the data store and must be a power of 2
+ """
+ def __init__(self, cachesize, master, slave):
+ self.master = master
+ self.slave = slave
+
+ ###
+
+ dw_from = flen(master.dat_r)
+ dw_to = flen(slave.dat_r)
+ if dw_to > dw_from and (dw_to % dw_from) != 0:
+ raise ValueError("Slave data width must be a multiple of {dw}".format(dw=dw_from))
+ if dw_to < dw_from and (dw_from % dw_to) != 0:
+ raise ValueError("Master data width must be a multiple of {dw}".format(dw=dw_to))
+
+ # Split address:
+ # TAG | LINE NUMBER | LINE OFFSET
+ offsetbits = log2_int(max(dw_to//dw_from, 1))
+ addressbits = flen(slave.adr) + offsetbits
+ linebits = log2_int(cachesize) - offsetbits
+ tagbits = addressbits - linebits
+ wordbits = log2_int(max(dw_from//dw_to, 1))
+ adr_offset, adr_line, adr_tag = split(master.adr, offsetbits, linebits, tagbits)
+ word = Signal(wordbits) if wordbits else None
+
+ # Data memory
+ data_mem = Memory(dw_to*2**wordbits, 2**linebits)
+ data_port = data_mem.get_port(write_capable=True, we_granularity=8)
+ self.specials += data_mem, data_port
+
+ write_from_slave = 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_slave,
+ displacer(slave.dat_r, word, data_port.dat_w),
+ displacer(Replicate(1, dw_to//8), word, data_port.we)
+ ).Else(
+ data_port.dat_w.eq(Replicate(master.dat_w, max(dw_to//dw_from, 1))),
+ If(master.cyc & master.stb & master.we & master.ack,
+ displacer(master.sel, adr_offset, data_port.we, 2**offsetbits, reverse=True)
+ )
+ ),
+ chooser(data_port.dat_r, word, slave.dat_w),
+ slave.sel.eq(2**(dw_to//8)-1),
+ chooser(data_port.dat_r, adr_offset_r, master.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 += slave.adr.eq(Cat(word, adr_line, tag_do.tag))
+ else:
+ self.comb += slave.adr.eq(Cat(adr_line, tag_do.tag))
+
+ # slave 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
+ self.submodules.fsm = fsm = FSM(reset_state="IDLE")
+ fsm.act("IDLE",
+ If(master.cyc & master.stb,
+ NextState("TEST_HIT")
+ )
+ )
+ fsm.act("TEST_HIT",
+ word_clr.eq(1),
+ If(tag_do.tag == adr_tag,
+ master.ack.eq(1),
+ If(master.we,
+ tag_di.dirty.eq(1),
+ tag_port.we.eq(1)
+ ),
+ NextState("IDLE")
+ ).Else(
+ If(tag_do.dirty,
+ NextState("EVICT")
+ ).Else(
+ NextState("REFILL_WRTAG")
+ )
+ )
+ )
+
+ fsm.act("EVICT",
+ slave.stb.eq(1),
+ slave.cyc.eq(1),
+ slave.we.eq(1),
+ If(slave.ack,
+ word_inc.eq(1),
+ If(word_is_last(word),
+ NextState("REFILL_WRTAG")
+ )
+ )
+ )
+ fsm.act("REFILL_WRTAG",
+ # Write the tag first to set the slave address
+ tag_port.we.eq(1),
+ word_clr.eq(1),
+ NextState("REFILL")
+ )
+ fsm.act("REFILL",
+ slave.stb.eq(1),
+ slave.cyc.eq(1),
+ slave.we.eq(0),
+ If(slave.ack,
+ write_from_slave.eq(1),
+ word_inc.eq(1),
+ If(word_is_last(word),
+ NextState("TEST_HIT"),
+ ).Else(
+ NextState("REFILL")
+ )
+ )
+ )
+
+
+class Tap(Module):
+ def __init__(self, bus, handler=print):
+ self.bus = bus
+ self.handler = handler
+
+ def do_simulation(self, selfp):
+ if selfp.bus.ack:
+ assert(selfp.bus.cyc and selfp.bus.stb)
+ if selfp.bus.we:
+ transaction = TWrite(selfp.bus.adr,
+ selfp.bus.dat_w,
+ selfp.bus.sel)
+ else:
+ transaction = TRead(selfp.bus.adr,
+ selfp.bus.dat_r)
+ self.handler(transaction)
+ do_simulation.passive = True
+
+
+class Initiator(Module):
+ def __init__(self, generator, bus=None):
+ self.generator = generator
+ if bus is None:
+ bus = Interface()
+ self.bus = bus
+ self.transaction_start = 0
+ self.transaction = None
+
+ def do_simulation(self, selfp):
+ if self.transaction is None or selfp.bus.ack:
+ 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
+ try:
+ self.transaction = next(self.generator)
+ except StopIteration:
+ selfp.bus.cyc = 0
+ selfp.bus.stb = 0
+ raise StopSimulation
+ if self.transaction is not None:
+ self.transaction_start = selfp.simulator.cycle_counter
+ selfp.bus.cyc = 1
+ selfp.bus.stb = 1
+ selfp.bus.adr = self.transaction.address
+ if isinstance(self.transaction, TWrite):
+ selfp.bus.we = 1
+ selfp.bus.sel = self.transaction.sel
+ selfp.bus.dat_w = self.transaction.data
+ else:
+ selfp.bus.we = 0
+ else:
+ selfp.bus.cyc = 0
+ selfp.bus.stb = 0
+
+
+class TargetModel:
+ def read(self, address):
+ return 0
+
+ def write(self, address, data, sel):
+ pass
+
+ def can_ack(self, bus):
+ return True
+
+
+class Target(Module):
+ def __init__(self, model, bus=None):
+ if bus is None:
+ bus = Interface()
+ self.bus = bus
+ self.model = model
+
+ def do_simulation(self, selfp):
+ bus = selfp.bus
+ if not bus.ack:
+ if self.model.can_ack(bus) and bus.cyc and bus.stb:
+ if bus.we:
+ self.model.write(bus.adr, bus.dat_w, bus.sel)
+ else:
+ bus.dat_r = self.model.read(bus.adr)
+ bus.ack = 1
+ else:
+ bus.ack = 0
+ do_simulation.passive = True
+
+
+class SRAM(Module):
+ def __init__(self, mem_or_size, read_only=None, init=None, bus=None):
+ if bus is None:
+ bus = Interface()
+ self.bus = bus
+ bus_data_width = flen(self.bus.dat_r)
+ if isinstance(mem_or_size, Memory):
+ assert(mem_or_size.width <= bus_data_width)
+ self.mem = mem_or_size
+ else:
+ self.mem = Memory(bus_data_width, mem_or_size//(bus_data_width//8), init=init)
+ if read_only is None:
+ if hasattr(self.mem, "bus_read_only"):
+ read_only = self.mem.bus_read_only
+ else:
+ read_only = False
+
+ ###
+
+ # memory
+ port = self.mem.get_port(write_capable=not read_only, we_granularity=8)
+ self.specials += self.mem, port
+ # generate write enable signal
+ if not read_only:
+ self.comb += [port.we[i].eq(self.bus.cyc & self.bus.stb & self.bus.we & self.bus.sel[i])
+ for i in range(4)]
+ # address and data
+ self.comb += [
+ port.adr.eq(self.bus.adr[:flen(port.adr)]),
+ self.bus.dat_r.eq(port.dat_r)
+ ]
+ if not read_only:
+ self.comb += port.dat_w.eq(self.bus.dat_w),
+ # generate ack
+ self.sync += [
+ self.bus.ack.eq(0),
+ If(self.bus.cyc & self.bus.stb & ~self.bus.ack, self.bus.ack.eq(1))
+ ]
+
+
+class CSRBank(csr.GenericBank):
+ def __init__(self, description, bus=None):
+ if bus is None:
+ bus = Interface()
+ self.bus = bus
+
+ ###
+
+ GenericBank.__init__(self, description, flen(self.bus.dat_w))
+
+ for i, c in enumerate(self.simple_csrs):
+ self.comb += [
+ c.r.eq(self.bus.dat_w[:c.size]),
+ c.re.eq(self.bus.cyc & self.bus.stb & ~self.bus.ack & self.bus.we & \
+ (self.bus.adr[:self.decode_bits] == i))
+ ]
+
+ brcases = dict((i, self.bus.dat_r.eq(c.w)) for i, c in enumerate(self.simple_csrs))
+ self.sync += [
+ Case(self.bus.adr[:self.decode_bits], brcases),
+ If(bus.ack, bus.ack.eq(0)).Elif(bus.cyc & bus.stb, bus.ack.eq(1))
+ ]
--- /dev/null
+from migen.fhdl.std import *
+from migen.bus import wishbone
+from migen.bus import csr
+from migen.genlib.misc import timeline
+
+
+class WB2CSR(Module):
+ def __init__(self, bus_wishbone=None, bus_csr=None):
+ if bus_wishbone is None:
+ bus_wishbone = wishbone.Interface()
+ self.wishbone = bus_wishbone
+ if bus_csr is None:
+ bus_csr = csr.Interface()
+ self.csr = bus_csr
+
+ ###
+
+ self.sync += [
+ self.csr.we.eq(0),
+ self.csr.dat_w.eq(self.wishbone.dat_w),
+ self.csr.adr.eq(self.wishbone.adr),
+ self.wishbone.dat_r.eq(self.csr.dat_r)
+ ]
+ self.sync += timeline(self.wishbone.cyc & self.wishbone.stb, [
+ (1, [self.csr.we.eq(self.wishbone.we)]),
+ (2, [self.wishbone.ack.eq(1)]),
+ (3, [self.wishbone.ack.eq(0)])
+ ])