interconnect: add bus/bank components from Migen
authorSebastien Bourdeauducq <sb@m-labs.hk>
Thu, 24 Sep 2015 12:48:18 +0000 (20:48 +0800)
committerSebastien Bourdeauducq <sb@m-labs.hk>
Thu, 24 Sep 2015 12:48:18 +0000 (20:48 +0800)
misoc/interconnect/__init__.py [new file with mode: 0644]
misoc/interconnect/csr.py [new file with mode: 0644]
misoc/interconnect/csr_bus.py [new file with mode: 0644]
misoc/interconnect/csr_eventmanager.py [new file with mode: 0644]
misoc/interconnect/lasmi_bus.py [new file with mode: 0644]
misoc/interconnect/lasmi_xbar.py [new file with mode: 0644]
misoc/interconnect/lasmibus.py [deleted file]
misoc/interconnect/lasmixbar.py [deleted file]
misoc/interconnect/wishbone.py [new file with mode: 0644]
misoc/interconnect/wishbone2csr.py [new file with mode: 0644]

diff --git a/misoc/interconnect/__init__.py b/misoc/interconnect/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/misoc/interconnect/csr.py b/misoc/interconnect/csr.py
new file mode 100644 (file)
index 0000000..861ed3e
--- /dev/null
@@ -0,0 +1,147 @@
+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)
diff --git a/misoc/interconnect/csr_bus.py b/misoc/interconnect/csr_bus.py
new file mode 100644 (file)
index 0000000..02c18d2
--- /dev/null
@@ -0,0 +1,215 @@
+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()]
diff --git a/misoc/interconnect/csr_eventmanager.py b/misoc/interconnect/csr_eventmanager.py
new file mode 100644 (file)
index 0000000..1edfcb0
--- /dev/null
@@ -0,0 +1,83 @@
+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]))
diff --git a/misoc/interconnect/lasmi_bus.py b/misoc/interconnect/lasmi_bus.py
new file mode 100644 (file)
index 0000000..acdb370
--- /dev/null
@@ -0,0 +1,164 @@
+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
diff --git a/misoc/interconnect/lasmi_xbar.py b/misoc/interconnect/lasmi_xbar.py
new file mode 100644 (file)
index 0000000..1af682e
--- /dev/null
@@ -0,0 +1,178 @@
+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
diff --git a/misoc/interconnect/lasmibus.py b/misoc/interconnect/lasmibus.py
deleted file mode 100644 (file)
index acdb370..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-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
diff --git a/misoc/interconnect/lasmixbar.py b/misoc/interconnect/lasmixbar.py
deleted file mode 100644 (file)
index 1af682e..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-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
diff --git a/misoc/interconnect/wishbone.py b/misoc/interconnect/wishbone.py
new file mode 100644 (file)
index 0000000..9f39f6a
--- /dev/null
@@ -0,0 +1,718 @@
+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))
+        ]
diff --git a/misoc/interconnect/wishbone2csr.py b/misoc/interconnect/wishbone2csr.py
new file mode 100644 (file)
index 0000000..2d43c77
--- /dev/null
@@ -0,0 +1,28 @@
+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)])
+        ])