Skip to content

Commit c4b340e

Browse files
committed
csr.bus: use proper enum instead of ad-hoc string enumeration.
1 parent d8f5c4c commit c4b340e

File tree

2 files changed

+31
-14
lines changed

2 files changed

+31
-14
lines changed

nmigen_soc/csr/bus.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
1-
from functools import reduce
1+
import enum
22
from nmigen import *
3-
from nmigen import tracer
43

54

65
__all__ = ["Element", "Interface", "Decoder"]
76

87

98
class Element(Record):
9+
class Access(enum.Enum):
10+
"""Register access mode.
11+
12+
Coarse access mode for the entire register. Individual fields can have more restrictive
13+
access mode, e.g. R/O fields can be a part of an R/W register.
14+
"""
15+
R = "r"
16+
W = "w"
17+
RW = "rw"
18+
19+
def readable(self):
20+
return self == self.R or self == self.RW
21+
22+
def writable(self):
23+
return self == self.W or self == self.RW
24+
1025
"""Peripheral-side CSR interface.
1126
1227
A low-level interface to a single atomically readable and writable register in a peripheral.
@@ -17,6 +32,8 @@ class Element(Record):
1732
----------
1833
width : int
1934
Width of the register.
35+
access : :class:`Access`
36+
Register access mode.
2037
name : str
2138
Name of the underlying record.
2239
@@ -37,19 +54,19 @@ def __init__(self, width, access, *, name=None, src_loc_at=0):
3754
if not isinstance(width, int) or width < 0:
3855
raise ValueError("Width must be a non-negative integer, not {!r}"
3956
.format(width))
40-
if access not in ("r", "w", "rw"):
57+
if not isinstance(access, Element.Access) and access not in ("r", "w", "rw"):
4158
raise ValueError("Access mode must be one of \"r\", \"w\", or \"rw\", not {!r}"
4259
.format(access))
4360
self.width = width
44-
self.access = access
61+
self.access = Element.Access(access)
4562

4663
layout = []
47-
if "r" in self.access:
64+
if self.access.readable():
4865
layout += [
4966
("r_data", width),
5067
("r_stb", 1),
5168
]
52-
if "w" in self.access:
69+
if self.access.writable():
5370
layout += [
5471
("w_data", width),
5572
("w_stb", 1),
@@ -240,7 +257,7 @@ def elaborate(self, platform):
240257

241258
for elem_addr, (elem, elem_size) in self._elements.items():
242259
shadow = Signal(elem.width, name="{}__shadow".format(elem.name))
243-
if "w" in elem.access:
260+
if elem.access.writable():
244261
m.d.comb += elem.w_data.eq(shadow)
245262

246263
# Enumerate every address used by the register explicitly, rather than using
@@ -252,7 +269,7 @@ def elaborate(self, platform):
252269
chunk_slice = slice(chunk_offset * self.bus.data_width,
253270
(chunk_offset + 1) * self.bus.data_width)
254271
with m.Case(elem_addr + chunk_offset):
255-
if "r" in elem.access:
272+
if elem.access.readable():
256273
chunk_r_stb = Signal(self.bus.data_width,
257274
name="{}__r_stb_{}".format(elem.name, chunk_offset))
258275
r_data_fanin |= Mux(chunk_r_stb, shadow[chunk_slice], 0)
@@ -263,7 +280,7 @@ def elaborate(self, platform):
263280
# Delay by 1 cycle, allowing reads to be pipelined.
264281
m.d.sync += chunk_r_stb.eq(self.bus.r_stb)
265282

266-
if "w" in elem.access:
283+
if elem.access.writable():
267284
if chunk_offset == elem_size - 1:
268285
# Delay by 1 cycle, avoiding combinatorial paths through
269286
# the CSR bus and into CSR registers.

nmigen_soc/test/test_csr_bus.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class ElementTestCase(unittest.TestCase):
1010
def test_layout_1_ro(self):
1111
elem = Element(1, "r")
1212
self.assertEqual(elem.width, 1)
13-
self.assertEqual(elem.access, "r")
13+
self.assertEqual(elem.access, Element.Access.R)
1414
self.assertEqual(elem.layout, Layout.cast([
1515
("r_data", 1),
1616
("r_stb", 1),
@@ -19,7 +19,7 @@ def test_layout_1_ro(self):
1919
def test_layout_8_rw(self):
2020
elem = Element(8, access="rw")
2121
self.assertEqual(elem.width, 8)
22-
self.assertEqual(elem.access, "rw")
22+
self.assertEqual(elem.access, Element.Access.RW)
2323
self.assertEqual(elem.layout, Layout.cast([
2424
("r_data", 8),
2525
("r_stb", 1),
@@ -30,16 +30,16 @@ def test_layout_8_rw(self):
3030
def test_layout_10_wo(self):
3131
elem = Element(10, "w")
3232
self.assertEqual(elem.width, 10)
33-
self.assertEqual(elem.access, "w")
33+
self.assertEqual(elem.access, Element.Access.W)
3434
self.assertEqual(elem.layout, Layout.cast([
3535
("w_data", 10),
3636
("w_stb", 1),
3737
]))
3838

3939
def test_layout_0_rw(self): # degenerate but legal case
40-
elem = Element(0, access="rw")
40+
elem = Element(0, access=Element.Access.RW)
4141
self.assertEqual(elem.width, 0)
42-
self.assertEqual(elem.access, "rw")
42+
self.assertEqual(elem.access, Element.Access.RW)
4343
self.assertEqual(elem.layout, Layout.cast([
4444
("r_data", 0),
4545
("r_stb", 1),

0 commit comments

Comments
 (0)