mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
New CSR API
This commit is contained in:
parent
633e5e6747
commit
c4f4143591
9 changed files with 201 additions and 328 deletions
|
@ -3,12 +3,11 @@ from migen.fhdl.module import Module
|
||||||
from migen.fhdl import verilog
|
from migen.fhdl import verilog
|
||||||
from migen.genlib.cdc import MultiReg
|
from migen.genlib.cdc import MultiReg
|
||||||
from migen.bank import description, csrgen
|
from migen.bank import description, csrgen
|
||||||
from migen.bank.description import READ_ONLY, WRITE_ONLY
|
|
||||||
|
|
||||||
class Example(Module):
|
class Example(Module):
|
||||||
def __init__(self, ninputs=32, noutputs=32):
|
def __init__(self, ninputs=32, noutputs=32):
|
||||||
r_o = description.RegisterField(noutputs, atomic_write=True)
|
r_o = description.CSRStorage(noutputs, atomic_write=True)
|
||||||
r_i = description.RegisterField(ninputs, READ_ONLY, WRITE_ONLY)
|
r_i = description.CSRStatus(ninputs)
|
||||||
|
|
||||||
self.submodules.bank = csrgen.Bank([r_o, r_i])
|
self.submodules.bank = csrgen.Bank([r_o, r_i])
|
||||||
self.gpio_in = Signal(ninputs)
|
self.gpio_in = Signal(ninputs)
|
||||||
|
@ -17,10 +16,10 @@ class Example(Module):
|
||||||
###
|
###
|
||||||
|
|
||||||
gpio_in_s = Signal(ninputs)
|
gpio_in_s = Signal(ninputs)
|
||||||
self.specials += MultiReg(self.gpio_in, gpio_in_s, "sys")
|
self.specials += MultiReg(self.gpio_in, gpio_in_s)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
r_i.field.w.eq(gpio_in_s),
|
self.gpio_out.eq(r_o.storage),
|
||||||
self.gpio_out.eq(r_o.field.r)
|
r_i.status.eq(gpio_in_s)
|
||||||
]
|
]
|
||||||
|
|
||||||
example = Example()
|
example = Example()
|
||||||
|
|
|
@ -18,16 +18,16 @@ def _convert_layout(layout):
|
||||||
r.append((element[0], element[1]))
|
r.append((element[0], element[1]))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def _create_registers_assign(layout, target, atomic, prefix=""):
|
def _create_csrs_assign(layout, target, atomic, prefix=""):
|
||||||
registers = []
|
csrs = []
|
||||||
assigns = []
|
assigns = []
|
||||||
for element in layout:
|
for element in layout:
|
||||||
if isinstance(element[1], list):
|
if isinstance(element[1], list):
|
||||||
r_registers, r_assigns = _create_registers_assign(element[1],
|
r_csrs, r_assigns = _create_csrs_assign(element[1],
|
||||||
atomic,
|
atomic,
|
||||||
getattr(target, element[0]),
|
getattr(target, element[0]),
|
||||||
element[0] + "_")
|
element[0] + "_")
|
||||||
registers += r_registers
|
csrs += r_csrs
|
||||||
assigns += r_assigns
|
assigns += r_assigns
|
||||||
else:
|
else:
|
||||||
name = element[0]
|
name = element[0]
|
||||||
|
@ -40,10 +40,10 @@ def _create_registers_assign(layout, target, atomic, prefix=""):
|
||||||
alignment = element[3]
|
alignment = element[3]
|
||||||
else:
|
else:
|
||||||
alignment = 0
|
alignment = 0
|
||||||
reg = RegisterField(nbits + alignment, reset=reset, atomic_write=atomic, name=prefix + name)
|
reg = CSRStorage(nbits + alignment, reset=reset, atomic_write=atomic, name=prefix + name)
|
||||||
registers.append(reg)
|
csrs.append(reg)
|
||||||
assigns.append(getattr(target, name).eq(reg.field.r[alignment:]))
|
assigns.append(getattr(target, name).eq(reg.storage[alignment:]))
|
||||||
return registers, assigns
|
return csrs, assigns
|
||||||
|
|
||||||
(MODE_EXTERNAL, MODE_SINGLE_SHOT, MODE_CONTINUOUS) = range(3)
|
(MODE_EXTERNAL, MODE_SINGLE_SHOT, MODE_CONTINUOUS) = range(3)
|
||||||
|
|
||||||
|
@ -51,23 +51,23 @@ class SingleGenerator(Actor):
|
||||||
def __init__(self, layout, mode):
|
def __init__(self, layout, mode):
|
||||||
self._mode = mode
|
self._mode = mode
|
||||||
Actor.__init__(self, ("source", Source, _convert_layout(layout)))
|
Actor.__init__(self, ("source", Source, _convert_layout(layout)))
|
||||||
self._registers, self._assigns = _create_registers_assign(layout,
|
self._csrs, self._assigns = _create_csrs_assign(layout,
|
||||||
self.token("source"), self._mode != MODE_SINGLE_SHOT)
|
self.token("source"), self._mode != MODE_SINGLE_SHOT)
|
||||||
if mode == MODE_EXTERNAL:
|
if mode == MODE_EXTERNAL:
|
||||||
self.trigger = Signal()
|
self.trigger = Signal()
|
||||||
elif mode == MODE_SINGLE_SHOT:
|
elif mode == MODE_SINGLE_SHOT:
|
||||||
shoot = RegisterRaw()
|
shoot = CSR()
|
||||||
self._registers.insert(0, shoot)
|
self._csrs.insert(0, shoot)
|
||||||
self.trigger = shoot.re
|
self.trigger = shoot.re
|
||||||
elif mode == MODE_CONTINUOUS:
|
elif mode == MODE_CONTINUOUS:
|
||||||
enable = RegisterField()
|
enable = CSRStorage()
|
||||||
self._registers.insert(0, enable)
|
self._csrs.insert(0, enable)
|
||||||
self.trigger = enable.field.r
|
self.trigger = enable.storage
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
def get_registers(self):
|
def get_csrs(self):
|
||||||
return self._registers
|
return self._csrs
|
||||||
|
|
||||||
def get_fragment(self):
|
def get_fragment(self):
|
||||||
stb = self.endpoints["source"].stb
|
stb = self.endpoints["source"].stb
|
||||||
|
@ -79,19 +79,16 @@ class SingleGenerator(Actor):
|
||||||
sync = [If(ack | ~stb, *stmts)]
|
sync = [If(ack | ~stb, *stmts)]
|
||||||
return Fragment(comb, sync)
|
return Fragment(comb, sync)
|
||||||
|
|
||||||
class Collector(Actor):
|
class Collector(Actor, AutoCSR):
|
||||||
def __init__(self, layout, depth=1024):
|
def __init__(self, layout, depth=1024):
|
||||||
Actor.__init__(self, ("sink", Sink, layout))
|
Actor.__init__(self, ("sink", Sink, layout))
|
||||||
self._depth = depth
|
self._depth = depth
|
||||||
self._dw = sum(len(s) for s in self.token("sink").flatten())
|
self._dw = sum(len(s) for s in self.token("sink").flatten())
|
||||||
|
|
||||||
self._r_wa = RegisterField(bits_for(self._depth-1), READ_WRITE, READ_WRITE)
|
self._r_wa = CSRStorage(bits_for(self._depth-1), write_from_dev=True)
|
||||||
self._r_wc = RegisterField(bits_for(self._depth), READ_WRITE, READ_WRITE, atomic_write=True)
|
self._r_wc = CSRStorage(bits_for(self._depth), write_from_dev=True, atomic_write=True)
|
||||||
self._r_ra = RegisterField(bits_for(self._depth-1), READ_WRITE, READ_ONLY)
|
self._r_ra = CSRStorage(bits_for(self._depth-1))
|
||||||
self._r_rd = RegisterField(self._dw, READ_ONLY, WRITE_ONLY)
|
self._r_rd = CSRStatus(self._dw)
|
||||||
|
|
||||||
def get_registers(self):
|
|
||||||
return [self._r_wa, self._r_wc, self._r_ra, self._r_rd]
|
|
||||||
|
|
||||||
def get_fragment(self):
|
def get_fragment(self):
|
||||||
mem = Memory(self._dw, self._depth)
|
mem = Memory(self._dw, self._depth)
|
||||||
|
@ -99,22 +96,22 @@ class Collector(Actor):
|
||||||
rp = mem.get_port()
|
rp = mem.get_port()
|
||||||
|
|
||||||
comb = [
|
comb = [
|
||||||
If(self._r_wc.field.r != 0,
|
If(self._r_wc.r != 0,
|
||||||
self.endpoints["sink"].ack.eq(1),
|
self.endpoints["sink"].ack.eq(1),
|
||||||
If(self.endpoints["sink"].stb,
|
If(self.endpoints["sink"].stb,
|
||||||
self._r_wa.field.we.eq(1),
|
self._r_wa.we.eq(1),
|
||||||
self._r_wc.field.we.eq(1),
|
self._r_wc.we.eq(1),
|
||||||
wp.we.eq(1)
|
wp.we.eq(1)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
self._r_wa.field.w.eq(self._r_wa.field.r + 1),
|
self._r_wa.dat_w.eq(self._r_wa.storage + 1),
|
||||||
self._r_wc.field.w.eq(self._r_wc.field.r - 1),
|
self._r_wc.dat_w.eq(self._r_wc.storage - 1),
|
||||||
|
|
||||||
wp.adr.eq(self._r_wa.field.r),
|
wp.adr.eq(self._r_wa.storage),
|
||||||
wp.dat_w.eq(Cat(*self.token("sink").flatten())),
|
wp.dat_w.eq(Cat(*self.token("sink").flatten())),
|
||||||
|
|
||||||
rp.adr.eq(self._r_ra.field.r),
|
rp.adr.eq(self._r_ra.storage),
|
||||||
self._r_rd.field.w.eq(rp.dat_r)
|
self._r_rd.status.eq(rp.dat_r)
|
||||||
]
|
]
|
||||||
|
|
||||||
return Fragment(comb, specials={mem})
|
return Fragment(comb, specials={mem})
|
||||||
|
|
|
@ -1,91 +1,51 @@
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
from migen.fhdl.structure import *
|
from migen.fhdl.structure import *
|
||||||
|
from migen.fhdl.module import Module
|
||||||
from migen.bus import csr
|
from migen.bus import csr
|
||||||
from migen.bank.description import *
|
from migen.bank.description import *
|
||||||
|
|
||||||
class Bank:
|
class Bank(Module):
|
||||||
def __init__(self, description, address=0, bus=None):
|
def __init__(self, description, address=0, bus=None):
|
||||||
self.description = description
|
|
||||||
self.address = address
|
|
||||||
if bus is None:
|
if bus is None:
|
||||||
bus = csr.Interface()
|
bus = csr.Interface()
|
||||||
self.bus = bus
|
self.bus = bus
|
||||||
|
|
||||||
def get_fragment(self):
|
|
||||||
comb = []
|
|
||||||
sync = []
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
if not description:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Turn description into simple CSRs and claim ownership of compound CSR modules
|
||||||
|
simple_csrs = []
|
||||||
|
for c in description:
|
||||||
|
if isinstance(c, CSR):
|
||||||
|
simple_csrs.append(c)
|
||||||
|
else:
|
||||||
|
c.finalize(csr.data_width)
|
||||||
|
simple_csrs += c.get_simple_csrs()
|
||||||
|
self.submodules += c
|
||||||
|
nbits = bits_for(len(simple_csrs)-1)
|
||||||
|
|
||||||
|
# Decode selection
|
||||||
sel = Signal()
|
sel = Signal()
|
||||||
comb.append(sel.eq(self.bus.adr[9:] == self.address))
|
self.comb += sel.eq(self.bus.adr[9:] == address)
|
||||||
|
|
||||||
desc_exp = expand_description(self.description, csr.data_width)
|
|
||||||
nbits = bits_for(len(desc_exp)-1)
|
|
||||||
|
|
||||||
# Bus writes
|
# Bus writes
|
||||||
bwcases = {}
|
for i, c in enumerate(simple_csrs):
|
||||||
for i, reg in enumerate(desc_exp):
|
self.comb += [
|
||||||
if isinstance(reg, RegisterRaw):
|
c.r.eq(self.bus.dat_w[:c.size]),
|
||||||
comb.append(reg.r.eq(self.bus.dat_w[:reg.size]))
|
c.re.eq(sel & \
|
||||||
comb.append(reg.re.eq(sel & \
|
|
||||||
self.bus.we & \
|
self.bus.we & \
|
||||||
(self.bus.adr[:nbits] == i)))
|
(self.bus.adr[:nbits] == i))
|
||||||
elif isinstance(reg, RegisterFields):
|
]
|
||||||
bwra = []
|
|
||||||
offset = 0
|
|
||||||
for field in reg.fields:
|
|
||||||
if field.access_bus == WRITE_ONLY or field.access_bus == READ_WRITE:
|
|
||||||
bwra.append(field.storage.eq(self.bus.dat_w[offset:offset+field.size]))
|
|
||||||
offset += field.size
|
|
||||||
if bwra:
|
|
||||||
bwcases[i] = bwra
|
|
||||||
# commit atomic writes
|
|
||||||
for field in reg.fields:
|
|
||||||
if isinstance(field, FieldAlias) and field.commit_list:
|
|
||||||
commit_instr = [hf.commit_to.eq(hf.storage) for hf in field.commit_list]
|
|
||||||
sync.append(If(sel & self.bus.we & self.bus.adr[:nbits] == i, *commit_instr))
|
|
||||||
else:
|
|
||||||
raise TypeError
|
|
||||||
if bwcases:
|
|
||||||
sync.append(If(sel & self.bus.we, Case(self.bus.adr[:nbits], bwcases)))
|
|
||||||
|
|
||||||
# Bus reads
|
# Bus reads
|
||||||
brcases = {}
|
brcases = dict((i, self.bus.dat_r.eq(c.w)) for i, c in enumerate(simple_csrs))
|
||||||
for i, reg in enumerate(desc_exp):
|
self.sync += [
|
||||||
if isinstance(reg, RegisterRaw):
|
self.bus.dat_r.eq(0),
|
||||||
brcases[i] = [self.bus.dat_r.eq(reg.w)]
|
If(sel, Case(self.bus.adr[:nbits], brcases))
|
||||||
elif isinstance(reg, RegisterFields):
|
]
|
||||||
brs = []
|
|
||||||
reg_readable = False
|
|
||||||
for field in reg.fields:
|
|
||||||
if field.access_bus == READ_ONLY or field.access_bus == READ_WRITE:
|
|
||||||
brs.append(field.storage)
|
|
||||||
reg_readable = True
|
|
||||||
else:
|
|
||||||
brs.append(Replicate(0, field.size))
|
|
||||||
if reg_readable:
|
|
||||||
brcases[i] = [self.bus.dat_r.eq(Cat(*brs))]
|
|
||||||
else:
|
|
||||||
raise TypeError
|
|
||||||
if brcases:
|
|
||||||
sync.append(self.bus.dat_r.eq(0))
|
|
||||||
sync.append(If(sel, Case(self.bus.adr[:nbits], brcases)))
|
|
||||||
else:
|
|
||||||
comb.append(self.bus.dat_r.eq(0))
|
|
||||||
|
|
||||||
# Device access
|
|
||||||
for reg in self.description:
|
|
||||||
if isinstance(reg, RegisterFields):
|
|
||||||
for field in reg.fields:
|
|
||||||
if field.access_bus == READ_ONLY and field.access_dev == WRITE_ONLY:
|
|
||||||
comb.append(field.storage.eq(field.w))
|
|
||||||
else:
|
|
||||||
if field.access_dev == READ_ONLY or field.access_dev == READ_WRITE:
|
|
||||||
comb.append(field.r.eq(field.storage))
|
|
||||||
if field.access_dev == WRITE_ONLY or field.access_dev == READ_WRITE:
|
|
||||||
sync.append(If(field.we, field.storage.eq(field.w)))
|
|
||||||
|
|
||||||
return Fragment(comb, sync)
|
|
||||||
|
|
||||||
# address_map(name, memory) returns the CSR offset at which to map
|
# address_map(name, memory) returns the CSR offset at which to map
|
||||||
# the CSR object (register bank or memory).
|
# the CSR object (register bank or memory).
|
||||||
|
@ -93,7 +53,7 @@ class Bank:
|
||||||
# Otherwise, it is a memory object belonging to 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
|
# address_map is called exactly once for each object at each call to
|
||||||
# scan(), so it can have side effects.
|
# scan(), so it can have side effects.
|
||||||
class BankArray:
|
class BankArray(Module):
|
||||||
def __init__(self, source, address_map):
|
def __init__(self, source, address_map):
|
||||||
self.source = source
|
self.source = source
|
||||||
self.address_map = address_map
|
self.address_map = address_map
|
||||||
|
@ -103,30 +63,29 @@ class BankArray:
|
||||||
self.banks = []
|
self.banks = []
|
||||||
self.srams = []
|
self.srams = []
|
||||||
for name, obj in sorted(self.source.__dict__.items(), key=itemgetter(0)):
|
for name, obj in sorted(self.source.__dict__.items(), key=itemgetter(0)):
|
||||||
if hasattr(obj, "get_registers"):
|
if hasattr(obj, "get_csrs"):
|
||||||
registers = obj.get_registers()
|
csrs = obj.get_csrs()
|
||||||
else:
|
else:
|
||||||
registers = []
|
csrs = []
|
||||||
if hasattr(obj, "get_memories"):
|
if hasattr(obj, "get_memories"):
|
||||||
memories = obj.get_memories()
|
memories = obj.get_memories()
|
||||||
for memory in memories:
|
for memory in memories:
|
||||||
mapaddr = self.address_map(name, memory)
|
mapaddr = self.address_map(name, memory)
|
||||||
mmap = csr.SRAM(memory, mapaddr)
|
mmap = csr.SRAM(memory, mapaddr)
|
||||||
registers += mmap.get_registers()
|
self.submodules += mmap
|
||||||
self.srams.append((name, memory, mmap))
|
csrs += mmap.get_csrs()
|
||||||
if registers:
|
self.srams.append((name, memory, mapaddr, mmap))
|
||||||
|
if csrs:
|
||||||
mapaddr = self.address_map(name, None)
|
mapaddr = self.address_map(name, None)
|
||||||
rmap = Bank(registers, mapaddr)
|
rmap = Bank(csrs, mapaddr)
|
||||||
self.banks.append((name, rmap))
|
self.submodules += rmap
|
||||||
|
self.banks.append((name, csrs, mapaddr, rmap))
|
||||||
|
|
||||||
def get_rmaps(self):
|
def get_rmaps(self):
|
||||||
return [rmap for name, rmap in self.banks]
|
return [rmap for name, csrs, mapaddr, rmap in self.banks]
|
||||||
|
|
||||||
def get_mmaps(self):
|
def get_mmaps(self):
|
||||||
return [mmap for name, memory, mmap in self.srams]
|
return [mmap for name, memory, mapaddr, mmap in self.srams]
|
||||||
|
|
||||||
def get_buses(self):
|
def get_buses(self):
|
||||||
return [i.bus for i in self.get_rmaps() + self.get_mmaps()]
|
return [i.bus for i in self.get_rmaps() + self.get_mmaps()]
|
||||||
|
|
||||||
def get_fragment(self):
|
|
||||||
return sum([i.get_fragment() for i in self.get_rmaps() + self.get_mmaps()], Fragment())
|
|
||||||
|
|
|
@ -1,70 +1,92 @@
|
||||||
from migen.fhdl.structure import *
|
from migen.fhdl.structure import *
|
||||||
from migen.fhdl.specials import Memory
|
from migen.fhdl.specials import Memory
|
||||||
|
from migen.fhdl.module import *
|
||||||
from migen.fhdl.tracer import get_obj_var_name
|
from migen.fhdl.tracer import get_obj_var_name
|
||||||
|
|
||||||
class _Register(HUID):
|
class _CSRBase(HUID):
|
||||||
def __init__(self, name):
|
def __init__(self, size, name):
|
||||||
HUID.__init__(self)
|
HUID.__init__(self)
|
||||||
self.name = get_obj_var_name(name)
|
self.name = get_obj_var_name(name)
|
||||||
if self.name is None:
|
if self.name is None:
|
||||||
raise ValueError("Cannot extract register name from code, need to specify.")
|
raise ValueError("Cannot extract CSR name from code, need to specify.")
|
||||||
if len(self.name) > 2 and self.name[:2] == "r_":
|
if len(self.name) > 2 and self.name[:2] == "r_":
|
||||||
self.name = self.name[2:]
|
self.name = self.name[2:]
|
||||||
|
self.size = size
|
||||||
|
|
||||||
class RegisterRaw(_Register):
|
class CSR(_CSRBase):
|
||||||
def __init__(self, size=1, name=None):
|
def __init__(self, size=1, name=None):
|
||||||
_Register.__init__(self, name)
|
_CSRBase.__init__(self, size, name)
|
||||||
self.size = size
|
self.re = Signal(name=self.name + "_re")
|
||||||
self.re = Signal()
|
self.r = Signal(self.size, name=self.name + "_r")
|
||||||
self.r = Signal(self.size)
|
self.w = Signal(self.size, name=self.name + "_w")
|
||||||
self.w = Signal(self.size)
|
|
||||||
|
|
||||||
def get_size(self):
|
class _CompoundCSR(_CSRBase, Module):
|
||||||
return self.size
|
def __init__(self, size, name):
|
||||||
|
_CSRBase.__init__(self, size, name)
|
||||||
|
self.simple_csrs = []
|
||||||
|
|
||||||
(READ_ONLY, WRITE_ONLY, READ_WRITE) = range(3)
|
def get_simple_csrs(self):
|
||||||
|
if not self.finalized:
|
||||||
|
raise FinalizeError
|
||||||
|
return self.simple_csrs
|
||||||
|
|
||||||
class Field:
|
def do_finalize(self, busword):
|
||||||
def __init__(self, size=1, access_bus=READ_WRITE, access_dev=READ_ONLY, reset=0, atomic_write=False, name=None):
|
raise NotImplementedError
|
||||||
self.name = get_obj_var_name(name)
|
|
||||||
if self.name is None:
|
class CSRStatus(_CompoundCSR):
|
||||||
raise ValueError("Cannot extract field name from code, need to specify.")
|
def __init__(self, size=1, name=None):
|
||||||
self.size = size
|
_CompoundCSR.__init__(self, size, name)
|
||||||
self.access_bus = access_bus
|
self.status = Signal(self.size)
|
||||||
self.access_dev = access_dev
|
|
||||||
|
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, name=None):
|
||||||
|
_CompoundCSR.__init__(self, size, name)
|
||||||
self.storage = Signal(self.size, reset=reset)
|
self.storage = Signal(self.size, reset=reset)
|
||||||
self.atomic_write = atomic_write
|
self.atomic_write = atomic_write
|
||||||
if self.access_bus == READ_ONLY and self.access_dev == WRITE_ONLY:
|
if write_from_dev:
|
||||||
self.w = Signal(self.size)
|
self.we = Signal()
|
||||||
else:
|
self.dat_w = Signal(self.size)
|
||||||
if self.access_dev == READ_ONLY or self.access_dev == READ_WRITE:
|
self.sync += If(self.we, self.storage.eq(self.dat_w))
|
||||||
self.r = Signal(self.size, reset=reset)
|
|
||||||
if self.access_dev == WRITE_ONLY or self.access_dev == READ_WRITE:
|
|
||||||
self.w = Signal(self.size)
|
|
||||||
self.we = Signal()
|
|
||||||
|
|
||||||
class RegisterFields(_Register):
|
def do_finalize(self, busword):
|
||||||
def __init__(self, *fields, name=None):
|
nwords = (self.size + busword - 1)//busword
|
||||||
_Register.__init__(self, name)
|
if nwords > 1 and self.atomic_write:
|
||||||
self.fields = fields
|
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)
|
||||||
|
lo = i*busword
|
||||||
|
hi = lo+nbits
|
||||||
|
# read
|
||||||
|
self.comb += sc.w.eq(self.storage[lo:hi])
|
||||||
|
# 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.eq(Cat(sc.r, backstore)))
|
||||||
|
else:
|
||||||
|
self.sync += If(sc.re, self.storage[lo:hi].eq(sc.r))
|
||||||
|
|
||||||
def get_size(self):
|
self.simple_csrs.append(sc)
|
||||||
return sum(field.size for field in self.fields)
|
|
||||||
|
|
||||||
class RegisterField(RegisterFields):
|
def csrprefix(prefix, csrs):
|
||||||
def __init__(self, size=1, access_bus=READ_WRITE, access_dev=READ_ONLY, reset=0, atomic_write=False, name=None):
|
for csr in csrs:
|
||||||
self.field = Field(size, access_bus, access_dev, reset, atomic_write, name="")
|
csr.name = prefix + csr.name
|
||||||
RegisterFields.__init__(self, self.field, name=name)
|
|
||||||
|
|
||||||
def regprefix(prefix, registers):
|
|
||||||
for register in registers:
|
|
||||||
register.name = prefix + register.name
|
|
||||||
|
|
||||||
def memprefix(prefix, memories):
|
def memprefix(prefix, memories):
|
||||||
for memory in memories:
|
for memory in memories:
|
||||||
memory.name_override = prefix + memory.name_override
|
memory.name_override = prefix + memory.name_override
|
||||||
|
|
||||||
class AutoReg:
|
class AutoCSR:
|
||||||
def get_memories(self):
|
def get_memories(self):
|
||||||
r = []
|
r = []
|
||||||
for k, v in self.__dict__.items():
|
for k, v in self.__dict__.items():
|
||||||
|
@ -76,88 +98,13 @@ class AutoReg:
|
||||||
r += memories
|
r += memories
|
||||||
return sorted(r, key=lambda x: x.huid)
|
return sorted(r, key=lambda x: x.huid)
|
||||||
|
|
||||||
def get_registers(self):
|
def get_csrs(self):
|
||||||
r = []
|
r = []
|
||||||
for k, v in self.__dict__.items():
|
for k, v in self.__dict__.items():
|
||||||
if isinstance(v, _Register):
|
if isinstance(v, _CSRBase):
|
||||||
r.append(v)
|
r.append(v)
|
||||||
elif hasattr(v, "get_registers") and callable(v.get_registers):
|
elif hasattr(v, "get_csrs") and callable(v.get_csrs):
|
||||||
registers = v.get_registers()
|
csrs = v.get_csrs()
|
||||||
regprefix(k + "_", registers)
|
csrprefix(k + "_", csrs)
|
||||||
r += registers
|
r += csrs
|
||||||
return sorted(r, key=lambda x: x.huid)
|
return sorted(r, key=lambda x: x.huid)
|
||||||
|
|
||||||
(ALIAS_NON_ATOMIC, ALIAS_ATOMIC_HOLD, ALIAS_ATOMIC_COMMIT) = range(3)
|
|
||||||
|
|
||||||
class FieldAlias:
|
|
||||||
def __init__(self, mode, f, start, end, commit_list):
|
|
||||||
self.mode = mode
|
|
||||||
self.size = end - start
|
|
||||||
self.access_bus = f.access_bus
|
|
||||||
self.access_dev = f.access_dev
|
|
||||||
if mode == ALIAS_ATOMIC_HOLD:
|
|
||||||
self.storage = Signal(end-start, name="atomic_hold")
|
|
||||||
self.commit_to = f.storage[start:end]
|
|
||||||
else:
|
|
||||||
self.storage = f.storage[start:end]
|
|
||||||
if mode == ALIAS_ATOMIC_COMMIT:
|
|
||||||
self.commit_list = commit_list
|
|
||||||
else:
|
|
||||||
self.commit_list = []
|
|
||||||
# device access is through the original field
|
|
||||||
|
|
||||||
def expand_description(description, busword):
|
|
||||||
d = []
|
|
||||||
for reg in description:
|
|
||||||
if isinstance(reg, RegisterRaw):
|
|
||||||
if reg.size > busword:
|
|
||||||
raise ValueError("Raw register larger than a bus word")
|
|
||||||
d.append(reg)
|
|
||||||
elif isinstance(reg, RegisterFields):
|
|
||||||
f = []
|
|
||||||
offset = 0
|
|
||||||
totalsize = 0
|
|
||||||
for field in reg.fields:
|
|
||||||
offset += field.size
|
|
||||||
totalsize += field.size
|
|
||||||
if offset > busword:
|
|
||||||
# add padding
|
|
||||||
padding = busword - (totalsize % busword)
|
|
||||||
if padding != busword:
|
|
||||||
totalsize += padding
|
|
||||||
offset += padding
|
|
||||||
|
|
||||||
top = field.size
|
|
||||||
commit_list = []
|
|
||||||
while offset > busword:
|
|
||||||
if field.atomic_write:
|
|
||||||
if offset - busword > busword:
|
|
||||||
mode = ALIAS_ATOMIC_HOLD
|
|
||||||
else:
|
|
||||||
# last iteration
|
|
||||||
mode = ALIAS_ATOMIC_COMMIT
|
|
||||||
else:
|
|
||||||
mode = ALIAS_NON_ATOMIC
|
|
||||||
|
|
||||||
slice1 = busword - offset + top
|
|
||||||
slice2 = min(offset - busword, busword)
|
|
||||||
if slice1:
|
|
||||||
alias = FieldAlias(mode, field, top - slice1, top, commit_list)
|
|
||||||
f.append(alias)
|
|
||||||
if mode == ALIAS_ATOMIC_HOLD:
|
|
||||||
commit_list.append(alias)
|
|
||||||
top -= slice1
|
|
||||||
d.append(RegisterFields(*f, name=reg.name))
|
|
||||||
alias = FieldAlias(mode, field, top - slice2, top, commit_list)
|
|
||||||
f = [alias]
|
|
||||||
if mode == ALIAS_ATOMIC_HOLD:
|
|
||||||
commit_list.append(alias)
|
|
||||||
top -= slice2
|
|
||||||
offset -= busword
|
|
||||||
else:
|
|
||||||
f.append(field)
|
|
||||||
if f:
|
|
||||||
d.append(RegisterFields(*f, name=reg.name))
|
|
||||||
else:
|
|
||||||
raise TypeError
|
|
||||||
return d
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ class EventSourcePulse(_EventSource):
|
||||||
class EventSourceLevel(_EventSource):
|
class EventSourceLevel(_EventSource):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class EventManager(Module, AutoReg):
|
class EventManager(Module, AutoCSR):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.irq = Signal()
|
self.irq = Signal()
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@ class EventManager(Module, AutoReg):
|
||||||
sources_u = [v for v in self.__dict__.values() if isinstance(v, _EventSource)]
|
sources_u = [v for v in self.__dict__.values() if isinstance(v, _EventSource)]
|
||||||
sources = sorted(sources_u, key=lambda x: x.huid)
|
sources = sorted(sources_u, key=lambda x: x.huid)
|
||||||
n = len(sources)
|
n = len(sources)
|
||||||
self.status = RegisterRaw(n)
|
self.status = CSR(n)
|
||||||
self.pending = RegisterRaw(n)
|
self.pending = CSR(n)
|
||||||
self.enable = RegisterFields(*(Field(1, READ_WRITE, READ_ONLY, name="e" + str(i)) for i in range(n)))
|
self.enable = CSRStorage(n)
|
||||||
|
|
||||||
# status
|
# status
|
||||||
for i, source in enumerate(sources):
|
for i, source in enumerate(sources):
|
||||||
|
@ -55,7 +55,7 @@ class EventManager(Module, AutoReg):
|
||||||
self.comb += self.pending.w[i].eq(source.pending)
|
self.comb += self.pending.w[i].eq(source.pending)
|
||||||
|
|
||||||
# IRQ
|
# IRQ
|
||||||
irqs = [self.pending.w[i] & field.r for i, field in enumerate(self.enable.fields)]
|
irqs = [self.pending.w[i] & self.enable.storage[i] for i in range(n)]
|
||||||
self.comb += self.irq.eq(optree("|", irqs))
|
self.comb += self.irq.eq(optree("|", irqs))
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
|
|
|
@ -3,7 +3,7 @@ from migen.fhdl.specials import Memory
|
||||||
from migen.fhdl.module import Module
|
from migen.fhdl.module import Module
|
||||||
from migen.bus.simple import *
|
from migen.bus.simple import *
|
||||||
from migen.bus.transactions import *
|
from migen.bus.transactions import *
|
||||||
from migen.bank.description import RegisterField
|
from migen.bank.description import CSRStorage
|
||||||
from migen.genlib.misc import chooser
|
from migen.genlib.misc import chooser
|
||||||
|
|
||||||
data_width = 8
|
data_width = 8
|
||||||
|
@ -68,7 +68,7 @@ class SRAM:
|
||||||
self.word_bits = 0
|
self.word_bits = 0
|
||||||
page_bits = _compute_page_bits(self.mem.depth + self.word_bits)
|
page_bits = _compute_page_bits(self.mem.depth + self.word_bits)
|
||||||
if page_bits:
|
if page_bits:
|
||||||
self._page = RegisterField(page_bits, name=self.mem.name_override + "_page")
|
self._page = CSRStorage(page_bits, name=self.mem.name_override + "_page")
|
||||||
else:
|
else:
|
||||||
self._page = None
|
self._page = None
|
||||||
if read_only is None:
|
if read_only is None:
|
||||||
|
@ -81,7 +81,7 @@ class SRAM:
|
||||||
bus = Interface()
|
bus = Interface()
|
||||||
self.bus = bus
|
self.bus = bus
|
||||||
|
|
||||||
def get_registers(self):
|
def get_csrs(self):
|
||||||
if self._page is None:
|
if self._page is None:
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
|
@ -126,7 +126,7 @@ class SRAM:
|
||||||
if self._page is None:
|
if self._page is None:
|
||||||
comb.append(port.adr.eq(self.bus.adr[self.word_bits:len(port.adr)]))
|
comb.append(port.adr.eq(self.bus.adr[self.word_bits:len(port.adr)]))
|
||||||
else:
|
else:
|
||||||
pv = self._page.field.r
|
pv = self._page.storage
|
||||||
comb.append(port.adr.eq(Cat(self.bus.adr[self.word_bits:len(port.adr)-len(pv)], pv)))
|
comb.append(port.adr.eq(Cat(self.bus.adr[self.word_bits:len(port.adr)-len(pv)], pv)))
|
||||||
|
|
||||||
return Fragment(comb, sync, specials={self.mem})
|
return Fragment(comb, sync, specials={self.mem})
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from migen.fhdl.structure import *
|
from migen.fhdl.structure import *
|
||||||
from migen.fhdl.module import Module
|
from migen.fhdl.module import Module
|
||||||
from migen.flow.actor import *
|
from migen.flow.actor import *
|
||||||
|
@ -24,23 +26,18 @@ class EndpointSimHook(Module):
|
||||||
else:
|
else:
|
||||||
self.on_inactive()
|
self.on_inactive()
|
||||||
|
|
||||||
class DFGHook:
|
class DFGHook(Module):
|
||||||
def __init__(self, dfg, create):
|
def __init__(self, dfg, create):
|
||||||
assert(not dfg.is_abstract())
|
assert(not dfg.is_abstract())
|
||||||
self.nodepair_to_ep = dict()
|
self.nodepair_to_ep = defaultdict(dict)
|
||||||
for u, v, data in dfg.edges_iter(data=True):
|
for hookn, (u, v, data) in dfg.edges_iter(data=True):
|
||||||
if (u, v) in self.nodepair_to_ep:
|
ep_to_hook = self.nodepair_to_ep[(u, v)]
|
||||||
ep_to_hook = self.nodepair_to_ep[(u, v)]
|
|
||||||
else:
|
|
||||||
ep_to_hook = dict()
|
|
||||||
self.nodepair_to_ep[(u, v)] = ep_to_hook
|
|
||||||
ep = data["source"]
|
ep = data["source"]
|
||||||
ep_to_hook[ep] = create(u, ep, v)
|
h = create(u, ep, v)
|
||||||
|
ep_to_hook[ep] = h
|
||||||
|
setattr(self.submodules, "hook"+str(hookn), h)
|
||||||
|
|
||||||
def hooks_iter(self):
|
def hooks_iter(self):
|
||||||
for v1 in self.nodepair_to_ep.values():
|
for v1 in self.nodepair_to_ep.values():
|
||||||
for v2 in v1.values():
|
for v2 in v1.values():
|
||||||
yield v2
|
yield v2
|
||||||
|
|
||||||
def get_fragment(self):
|
|
||||||
return sum([h.get_fragment() for h in self.hooks_iter()], Fragment())
|
|
||||||
|
|
|
@ -1,38 +1,30 @@
|
||||||
from migen.fhdl.structure import *
|
from migen.fhdl.structure import *
|
||||||
|
from migen.fhdl.module import Module
|
||||||
from migen.bank.description import *
|
from migen.bank.description import *
|
||||||
from migen.flow.hooks import DFGHook
|
from migen.flow.hooks import DFGHook
|
||||||
|
|
||||||
ISD_MAGIC = 0x6ab4
|
ISD_MAGIC = 0x6ab4
|
||||||
|
|
||||||
class EndpointReporter:
|
class EndpointReporter(Module, AutoCSR):
|
||||||
def __init__(self, endpoint, nbits):
|
def __init__(self, endpoint, nbits):
|
||||||
self.endpoint = endpoint
|
|
||||||
self.nbits = nbits
|
|
||||||
self.reset = Signal()
|
self.reset = Signal()
|
||||||
self.freeze = Signal()
|
self.freeze = Signal()
|
||||||
|
|
||||||
self._ack_count = RegisterField(self.nbits, READ_ONLY, WRITE_ONLY)
|
self._ack_count = CSRStatus(nbits)
|
||||||
self._nack_count = RegisterField(self.nbits, READ_ONLY, WRITE_ONLY)
|
self._nack_count = CSRStatus(nbits)
|
||||||
self._cur_stb = Field(1, READ_ONLY, WRITE_ONLY)
|
self._cur_status = CSRStatus(2)
|
||||||
self._cur_ack = Field(1, READ_ONLY, WRITE_ONLY)
|
|
||||||
self._cur_status = RegisterFields(self._cur_stb, self._cur_ack)
|
|
||||||
|
|
||||||
def get_registers(self):
|
###
|
||||||
return [self._ack_count, self._nack_count, self._cur_status]
|
|
||||||
|
|
||||||
def get_fragment(self):
|
|
||||||
stb = Signal()
|
stb = Signal()
|
||||||
ack = Signal()
|
ack = Signal()
|
||||||
ack_count = Signal(self.nbits)
|
self.comb += self._cur_status.status.eq(Cat(stb, ack))
|
||||||
nack_count = Signal(self.nbits)
|
ack_count = Signal(nbits)
|
||||||
comb = [
|
nack_count = Signal(nbits)
|
||||||
self._cur_stb.w.eq(stb),
|
self.sync += [
|
||||||
self._cur_ack.w.eq(ack)
|
|
||||||
]
|
|
||||||
sync = [
|
|
||||||
# register monitored signals
|
# register monitored signals
|
||||||
stb.eq(self.endpoint.stb),
|
stb.eq(endpoint.stb),
|
||||||
ack.eq(self.endpoint.ack),
|
ack.eq(endpoint.ack),
|
||||||
# count operations
|
# count operations
|
||||||
If(self.reset,
|
If(self.reset,
|
||||||
ack_count.eq(0),
|
ack_count.eq(0),
|
||||||
|
@ -47,49 +39,31 @@ class EndpointReporter:
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
If(~self.freeze,
|
If(~self.freeze,
|
||||||
self._ack_count.field.w.eq(ack_count),
|
self._ack_count.status.eq(ack_count),
|
||||||
self._nack_count.field.w.eq(nack_count)
|
self._nack_count.status.eq(nack_count)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
return Fragment(comb, sync)
|
|
||||||
|
|
||||||
class DFGReporter(DFGHook):
|
class DFGReporter(DFGHook, AutoCSR):
|
||||||
def __init__(self, dfg, nbits):
|
def __init__(self, dfg, nbits):
|
||||||
self._nbits = nbits
|
self._r_magic = CSRStatus(16)
|
||||||
|
self._r_neps = CSRStatus(8)
|
||||||
|
self._r_nbits = CSRStatus(8)
|
||||||
|
self._r_freeze = CSRStorage()
|
||||||
|
self._r_reset = CSR()
|
||||||
|
|
||||||
self._r_magic = RegisterField(16, access_bus=READ_ONLY, access_dev=WRITE_ONLY)
|
###
|
||||||
self._r_neps = RegisterField(8, access_bus=READ_ONLY, access_dev=WRITE_ONLY)
|
|
||||||
self._r_nbits = RegisterField(8, access_bus=READ_ONLY, access_dev=WRITE_ONLY)
|
DFGHook.__init__(self, dfg,
|
||||||
self._r_freeze = RegisterField()
|
lambda u, ep, v: EndpointReporter(u.endpoints[ep], nbits))
|
||||||
self._r_reset = RegisterRaw()
|
|
||||||
|
self.comb += [
|
||||||
self.order = []
|
self._r_magic.status.eq(ISD_MAGIC),
|
||||||
DFGHook.__init__(self, dfg, self._create)
|
self._r_neps.status.eq(len(self.hooks_iter())),
|
||||||
|
self._r_nbits.status.eq(nbits)
|
||||||
def _create(self, u, ep, v):
|
|
||||||
self.order.append((u, ep, v))
|
|
||||||
return EndpointReporter(u.actor.endpoints[ep], self._nbits)
|
|
||||||
|
|
||||||
def print_map(self):
|
|
||||||
for n, (u, ep, v) in enumerate(self.order):
|
|
||||||
print("#" + str(n) + ": " + str(u) + ":" + ep + " -> " + str(v))
|
|
||||||
|
|
||||||
def get_registers(self):
|
|
||||||
registers = [self._r_magic, self._r_neps, self._r_nbits,
|
|
||||||
self._r_freeze, self._r_reset]
|
|
||||||
for u, ep, v in self.order:
|
|
||||||
registers += self.nodepair_to_ep[(u, v)][ep].get_registers()
|
|
||||||
return registers
|
|
||||||
|
|
||||||
def get_fragment(self):
|
|
||||||
comb = [
|
|
||||||
self._r_magic.field.w.eq(ISD_MAGIC),
|
|
||||||
self._r_neps.field.w.eq(len(self.order)),
|
|
||||||
self._r_nbits.field.w.eq(self._nbits)
|
|
||||||
]
|
]
|
||||||
for h in self.hooks_iter():
|
for h in self.hooks_iter():
|
||||||
comb += [
|
self.comb += [
|
||||||
h.freeze.eq(self._r_freeze.field.r),
|
h.freeze.eq(self._r_freeze.storage),
|
||||||
h.reset.eq(self._r_reset.re)
|
h.reset.eq(self._r_reset.re)
|
||||||
]
|
]
|
||||||
return Fragment(comb) + DFGHook.get_fragment(self)
|
|
||||||
|
|
|
@ -211,9 +211,9 @@ class CompositeActor(Actor):
|
||||||
self.debugger = DFGReporter(self.dfg, debugger_nbits)
|
self.debugger = DFGReporter(self.dfg, debugger_nbits)
|
||||||
Actor.__init__(self)
|
Actor.__init__(self)
|
||||||
|
|
||||||
def get_registers(self):
|
def get_csrs(self):
|
||||||
if hasattr(self, "debugger"):
|
if hasattr(self, "debugger"):
|
||||||
return self.debugger.get_registers()
|
return self.debugger.get_csrs()
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue