From 3a2a0c4dd80c40cd34b783113c058f5047e6e793 Mon Sep 17 00:00:00 2001
From: Sebastien Bourdeauducq <sebastien@milkymist.org>
Date: Mon, 6 Feb 2012 16:15:27 +0100
Subject: [PATCH] bank: support registers larger than the bus word width

---
 examples/simple_gpio.py   |  2 +-
 migen/bank/csrgen.py      | 17 ++++++++++-------
 migen/bank/description.py | 40 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/examples/simple_gpio.py b/examples/simple_gpio.py
index 426df02c..b53a323a 100644
--- a/examples/simple_gpio.py
+++ b/examples/simple_gpio.py
@@ -3,7 +3,7 @@ from migen.fhdl import verilog
 from migen.bank import description, csrgen
 
 ninputs = 4
-noutputs = 4
+noutputs = 31
 
 oreg = description.RegisterField("o", noutputs)
 ireg = description.RegisterRaw("i", ninputs)
diff --git a/migen/bank/csrgen.py b/migen/bank/csrgen.py
index 89457935..6007ab50 100644
--- a/migen/bank/csrgen.py
+++ b/migen/bank/csrgen.py
@@ -15,11 +15,12 @@ class Bank:
 		sel = Signal()
 		comb.append(sel.eq(self.interface.a_i[9:] == Constant(self.address, BV(5))))
 		
-		nbits = bits_for(len(self.description)-1)
+		desc_exp = expand_description(self.description, 8)
+		nbits = bits_for(len(desc_exp)-1)
 		
 		# Bus writes
 		bwcases = []
-		for i, reg in enumerate(self.description):
+		for i, reg in enumerate(desc_exp):
 			if isinstance(reg, RegisterRaw):
 				comb.append(reg.r.eq(self.interface.d_i[:reg.size]))
 				comb.append(reg.re.eq(sel & \
@@ -27,9 +28,11 @@ class Bank:
 					(self.interface.a_i[:nbits] == Constant(i, BV(nbits)))))
 			elif isinstance(reg, RegisterFields):
 				bwra = [Constant(i, BV(nbits))]
-				for j, field in enumerate(reg.fields):
+				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.interface.d_i[j]))
+						bwra.append(field.storage.eq(self.interface.d_i[offset:offset+field.size]))
+					offset += field.size
 				if len(bwra) > 1:
 					bwcases.append(bwra)
 			else:
@@ -39,13 +42,13 @@ class Bank:
 		
 		# Bus reads
 		brcases = []
-		for i, reg in enumerate(self.description):
+		for i, reg in enumerate(desc_exp):
 			if isinstance(reg, RegisterRaw):
 				brcases.append([Constant(i, BV(nbits)), self.interface.d_o.eq(reg.w)])
 			elif isinstance(reg, RegisterFields):
 				brs = []
 				reg_readable = False
-				for j, field in enumerate(reg.fields):
+				for field in reg.fields:
 					if field.access_bus == READ_ONLY or field.access_bus == READ_WRITE:
 						brs.append(field.storage)
 						reg_readable = True
@@ -53,7 +56,7 @@ class Bank:
 						brs.append(Constant(0, BV(field.size)))
 				if reg_readable:
 					if len(brs) > 1:
-						brcases.append([Constant(i, BV(nbits)), self.interface.d_o.eq(f.Cat(*brs))])
+						brcases.append([Constant(i, BV(nbits)), self.interface.d_o.eq(Cat(*brs))])
 					else:
 						brcases.append([Constant(i, BV(nbits)), self.interface.d_o.eq(brs[0])])
 			else:
diff --git a/migen/bank/description.py b/migen/bank/description.py
index 14d2d136..325738f7 100644
--- a/migen/bank/description.py
+++ b/migen/bank/description.py
@@ -32,3 +32,43 @@ 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)
 		RegisterFields.__init__(self, name, [self.field])
+
+class FieldAlias:
+	def __init__(self, f, start, end):
+		self.size = end - start
+		self.access_bus = f.access_bus
+		self.access_dev = f.access_dev
+		self.storage = f.storage[start:end]
+		# 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 = []
+			size = 0
+			for field in reg.fields:
+				size += field.size
+				if size > busword:
+					top = field.size
+					while size > busword:
+						slice1 = busword - size + top
+						slice2 = min(size - busword, busword)
+						if slice1:
+							f.append(FieldAlias(field, top - slice1, top))
+							top -= slice1
+						d.append(RegisterFields(reg.name, f))
+						f = [FieldAlias(field, top - slice2, top)]
+						top -= slice2
+						size -= busword
+				else:
+					f.append(field)
+			if f:
+				d.append(RegisterFields(reg.name, f))
+		else:
+			raise TypeError
+	return d
-- 
2.30.2