From 8c080e5fb6cf52342e4e4b5963d9d85e2e90a57a Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 13 Sep 2019 20:01:31 +0200 Subject: [PATCH] soc/interconnect/csr: add initial field support --- litex/soc/integration/cpu_interface.py | 4 ++ litex/soc/interconnect/csr.py | 67 +++++++++++++++++++++++++- test/test_csr.py | 28 +++++++++++ 3 files changed, 97 insertions(+), 2 deletions(-) diff --git a/litex/soc/integration/cpu_interface.py b/litex/soc/integration/cpu_interface.py index 3ac6066ef..0134fc9fe 100644 --- a/litex/soc/integration/cpu_interface.py +++ b/litex/soc/integration/cpu_interface.py @@ -181,6 +181,10 @@ def get_csr_header(regions, constants, with_access_functions=True, with_shadow_b r += _get_rw_functions_c(name + "_" + csr.name, origin, nr, busword, alignment, isinstance(csr, CSRStatus), with_access_functions) origin += alignment//8*nr + if hasattr(csr, "fields"): + for field in csr.fields.fields: + r += "#define CSR_"+name.upper()+"_"+csr.name.upper()+"_"+field.name.upper()+"_OFFSET "+str(field.offset)+"\n" + r += "#define CSR_"+name.upper()+"_"+csr.name.upper()+"_"+field.name.upper()+"_SIZE "+str(field.size)+"\n" r += "\n/* constants */\n" for name, value in constants: diff --git a/litex/soc/interconnect/csr.py b/litex/soc/interconnect/csr.py index 4165dbd24..03464e486 100644 --- a/litex/soc/interconnect/csr.py +++ b/litex/soc/interconnect/csr.py @@ -122,6 +122,57 @@ class _CompoundCSR(_CSRBase, Module): raise NotImplementedError +class CSRField(Signal): + def __init__(self, name, size=1, offset=None, reset=0, description=None, values=None): + assert name == name.lower() + self.name = name + self.size = size + self.offset = offset + self.reset_value = reset + self.description = description + self.values = values + Signal.__init__(self, size, name=name, reset=reset) + + +class CSRFieldCompound: + def __init__(self, fields): + self.check_names(fields) + self.check_ordering_overlap(fields) + self.fields = fields + for field in fields: + setattr(self, field.name, field) + + @staticmethod + def check_names(fields): + names = [] + for field in fields: + if field.name in names: + raise ValueError("CSRField \"{}\" name is already used in CSR".format(field.name)) + else: + names.append(field.name) + + @staticmethod + def check_ordering_overlap(fields): + offset = 0 + for field in fields: + if field.offset is not None: + if field.offset < offset: + raise ValueError("CSRField ordering/overlap issue on \"{}\" field".format(field.name)) + offset = field.offset + else: + field.offset = offset + offset += field.size + + def get_size(self): + return self.fields[-1].offset + self.fields[-1].size + + def get_reset(self): + reset = 0 + for field in self.fields: + reset |= (field.reset_value << field.offset) + return reset + + class CSRStatus(_CompoundCSR): """Status Register. @@ -156,9 +207,15 @@ class CSRStatus(_CompoundCSR): The value of the CSRStatus register. """ - def __init__(self, size=1, reset=0, name=None): + def __init__(self, size=1, reset=0, fields=[], name=None, description=None): + if fields != []: + self.fields = CSRFieldCompound(fields) + size = self.fields.get_size() + reset = self.fields.get_reset() _CompoundCSR.__init__(self, size, name) self.status = Signal(self.size, reset=reset) + for field in fields: + self.comb += self.status[field.offset:field.offset + field.size].eq(getattr(self.fields, field.name)) def do_finalize(self, busword): nwords = (self.size + busword - 1)//busword @@ -227,7 +284,11 @@ class CSRStorage(_CompoundCSR): ??? """ - def __init__(self, size=1, reset=0, atomic_write=False, write_from_dev=False, alignment_bits=0, name=None): + def __init__(self, size=1, reset=0, fields=[], atomic_write=False, write_from_dev=False, alignment_bits=0, name=None, description=None): + if fields != []: + self.fields = CSRFieldCompound(fields) + size = self.fields.get_size() + reset = self.fields.get_reset() _CompoundCSR.__init__(self, size, name) self.alignment_bits = alignment_bits self.storage_full = Signal(self.size, reset=reset) @@ -239,6 +300,8 @@ class CSRStorage(_CompoundCSR): 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)) + for field in [*fields]: + self.comb += getattr(self.fields, field.name).eq(self.storage[field.offset:field.offset + field.size]) def do_finalize(self, busword): nwords = (self.size + busword - 1)//busword diff --git a/test/test_csr.py b/test/test_csr.py index a5958650d..38a4fe1e3 100644 --- a/test/test_csr.py +++ b/test/test_csr.py @@ -94,3 +94,31 @@ class TestCSR(unittest.TestCase): dut = CSRDUT() run_simulation(dut, generator(dut)) + + def test_csr_fields(self): + def generator(dut): + # check reset values + self.assertEqual((yield dut._storage.fields.foo), 0xa) + self.assertEqual((yield dut._storage.fields.bar), 0x5a) + self.assertEqual((yield dut._storage.storage), 0x5a000a) + yield + yield + self.assertEqual((yield dut._status.fields.foo), 0xa) + self.assertEqual((yield dut._status.fields.bar), 0x5a) + + class DUT(Module): + def __init__(self): + self._storage = csr.CSRStorage(fields=[ + csr.CSRField("foo", size=4, offset=0, reset=0xa, description="foo"), + csr.CSRField("bar", size=8, offset=16, reset=0x5a, description="bar") + ]) + self._status = csr.CSRStatus(fields=[ + csr.CSRField("foo", size=4, offset=4, description="foo"), + csr.CSRField("bar", size=8, offset=8, description="bar") + ]) + self.comb += [ + self._status.fields.foo.eq(self._storage.fields.foo), + self._status.fields.bar.eq(self._storage.fields.bar), + ] + dut = DUT() + run_simulation(dut, generator(dut))