bank: support for atomic writes
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Mon, 8 Oct 2012 16:43:18 +0000 (18:43 +0200)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Mon, 8 Oct 2012 16:43:18 +0000 (18:43 +0200)
examples/basic/simple_gpio.py
migen/bank/csrgen.py
migen/bank/description.py

index 1a7387914dfbda53aa0bb8a405873e79733a4e80..d469eec285ef7734c2994c99688c6d0b4b18316e 100644 (file)
@@ -6,7 +6,7 @@ from migen.bank.description import READ_ONLY, WRITE_ONLY
 ninputs = 32
 noutputs = 32
 
-oreg = description.RegisterField("o", noutputs)
+oreg = description.RegisterField("o", noutputs, atomic_write=True)
 ireg = description.RegisterField("i", ninputs, READ_ONLY, WRITE_ONLY)
 
 # input path
index ec9a4148ddcf955f3f93f8482fe9e693d4124221..140e9baa09c866cb5f82651b8aed16af89f5aa23 100644 (file)
@@ -39,6 +39,11 @@ class Bank:
                                if len(bwra) > 1:
                                        bwra.append(reg.re.eq(1))
                                        bwcases.append(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.interface.we & self.interface.adr[:nbits] == i, *commit_instr))
                        else:
                                raise TypeError
                if bwcases:
@@ -59,10 +64,7 @@ class Bank:
                                        else:
                                                brs.append(Constant(0, BV(field.size)))
                                if reg_readable:
-                                       if len(brs) > 1:
-                                               brcases.append([Constant(i, BV(nbits)), self.interface.dat_r.eq(Cat(*brs))])
-                                       else:
-                                               brcases.append([Constant(i, BV(nbits)), self.interface.dat_r.eq(brs[0])])
+                                       brcases.append([Constant(i, BV(nbits)), self.interface.dat_r.eq(Cat(*brs))])
                        else:
                                raise TypeError
                if brcases:
index 7f79ca6a8511e8a0b1497839f011ef3633e4d9c5..854e19d61ed2a16c676c56bd3bbe55d1011b1709 100644 (file)
@@ -11,12 +11,13 @@ class RegisterRaw:
 (READ_ONLY, WRITE_ONLY, READ_WRITE) = range(3)
 
 class Field:
-       def __init__(self, name, size=1, access_bus=READ_WRITE, access_dev=READ_ONLY, reset=0):
+       def __init__(self, name, size=1, access_bus=READ_WRITE, access_dev=READ_ONLY, reset=0, atomic_write=False):
                self.name = name
                self.size = size
                self.access_bus = access_bus
                self.access_dev = access_dev
                self.storage = Signal(BV(self.size), reset=reset)
+               self.atomic_write = atomic_write
                if self.access_bus == READ_ONLY and self.access_dev == WRITE_ONLY:
                        self.w = Signal(BV(self.size))
                else:
@@ -36,16 +37,27 @@ class RegisterFields:
                        self.re = re
 
 class RegisterField(RegisterFields):
-       def __init__(self, name, size=1, access_bus=READ_WRITE, access_dev=READ_ONLY, reset=0):
-               self.field = Field(name, size, access_bus, access_dev, reset)
+       def __init__(self, name, size=1, access_bus=READ_WRITE, access_dev=READ_ONLY, reset=0, atomic_write=False):
+               self.field = Field(name, size, access_bus, access_dev, reset, atomic_write)
                super().__init__(name, [self.field])
 
+(ALIAS_NON_ATOMIC, ALIAS_ATOMIC_HOLD, ALIAS_ATOMIC_COMMIT) = range(3)
+
 class FieldAlias:
-       def __init__(self, f, start, end):
+       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
-               self.storage = f.storage[start:end]
+               if mode == ALIAS_ATOMIC_HOLD:
+                       self.storage = Signal(BV(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):
@@ -57,28 +69,45 @@ def expand_description(description, busword):
                        d.append(reg)
                elif isinstance(reg, RegisterFields):
                        f = []
-                       size = 0
+                       offset = 0
+                       totalsize = 0
                        for field in reg.fields:
-                               size += field.size
-                               if size > busword:
+                               offset += field.size
+                               totalsize += field.size
+                               if offset > busword:
                                        # add padding
-                                       totalsize = sum([field.size for field in reg.fields])
                                        padding = busword - (totalsize % busword)
-                                       if padding == busword:
-                                               padding = 0
-                                       size += padding
+                                       if padding != busword:
+                                               totalsize += padding
+                                               offset += padding
                                        
                                        top = field.size
-                                       while size > busword:
-                                               slice1 = busword - size + top
-                                               slice2 = min(size - busword, busword)
+                                       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:
-                                                       f.append(FieldAlias(field, top - slice1, top))
+                                                       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(reg.name, f))
-                                               f = [FieldAlias(field, top - slice2, top)]
+                                               alias = FieldAlias(mode, field, top - slice2, top, commit_list)
+                                               f = [alias]
+                                               if mode == ALIAS_ATOMIC_HOLD:
+                                                       commit_list.append(alias)
                                                top -= slice2
-                                               size -= busword
+                                               offset -= busword
                                else:
                                        f.append(field)
                        if f: