-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
interconnect: add bus/bank components from Migen
1 parent
ecdc410
commit f69674e
Showing
8 changed files
with
1,191 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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])) |
File renamed without changes.
File renamed without changes.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)]) | ||
]) |