From f69674e89c9ace5e752c3bea4387067bf4a5b4a0 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 24 Sep 2015 20:48:18 +0800 Subject: [PATCH] interconnect: add bus/bank components from Migen --- misoc/interconnect/__init__.py | 0 misoc/interconnect/csr.py | 147 ++++ misoc/interconnect/csr_bus.py | 215 ++++++ misoc/interconnect/csr_eventmanager.py | 83 ++ .../{lasmibus.py => lasmi_bus.py} | 0 .../{lasmixbar.py => lasmi_xbar.py} | 0 misoc/interconnect/wishbone.py | 718 ++++++++++++++++++ misoc/interconnect/wishbone2csr.py | 28 + 8 files changed, 1191 insertions(+) create mode 100644 misoc/interconnect/__init__.py create mode 100644 misoc/interconnect/csr.py create mode 100644 misoc/interconnect/csr_bus.py create mode 100644 misoc/interconnect/csr_eventmanager.py rename misoc/interconnect/{lasmibus.py => lasmi_bus.py} (100%) rename misoc/interconnect/{lasmixbar.py => lasmi_xbar.py} (100%) create mode 100644 misoc/interconnect/wishbone.py create mode 100644 misoc/interconnect/wishbone2csr.py diff --git a/misoc/interconnect/__init__.py b/misoc/interconnect/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misoc/interconnect/csr.py b/misoc/interconnect/csr.py new file mode 100644 index 00000000..861ed3e4 --- /dev/null +++ b/misoc/interconnect/csr.py @@ -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 index 00000000..02c18d2b --- /dev/null +++ b/misoc/interconnect/csr_bus.py @@ -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 index 00000000..1edfcb04 --- /dev/null +++ b/misoc/interconnect/csr_eventmanager.py @@ -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/lasmibus.py b/misoc/interconnect/lasmi_bus.py similarity index 100% rename from misoc/interconnect/lasmibus.py rename to misoc/interconnect/lasmi_bus.py diff --git a/misoc/interconnect/lasmixbar.py b/misoc/interconnect/lasmi_xbar.py similarity index 100% rename from misoc/interconnect/lasmixbar.py rename to misoc/interconnect/lasmi_xbar.py diff --git a/misoc/interconnect/wishbone.py b/misoc/interconnect/wishbone.py new file mode 100644 index 00000000..9f39f6a0 --- /dev/null +++ b/misoc/interconnect/wishbone.py @@ -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 index 00000000..2d43c77e --- /dev/null +++ b/misoc/interconnect/wishbone2csr.py @@ -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)]) + ]) -- 2.30.2