wishbone.bus: add Interface.
[nmigen-soc.git] / nmigen_soc / wishbone / bus.py
1 from enum import Enum
2 from nmigen import *
3 from nmigen.hdl.rec import Direction
4 from nmigen.utils import log2_int
5
6 from ..memory import MemoryMap
7
8
9 __all__ = ["CycleType", "BurstTypeExt", "Interface"]
10
11
12 class CycleType(Enum):
13 """Wishbone Registered Feedback cycle type."""
14 CLASSIC = 0b000
15 CONST_BURST = 0b001
16 INCR_BURST = 0b010
17 END_OF_BURST = 0b111
18
19
20 class BurstTypeExt(Enum):
21 """Wishbone Registered Feedback burst type extension."""
22 LINEAR = 0b00
23 WRAP_4 = 0b01
24 WRAP_8 = 0b10
25 WRAP_16 = 0b11
26
27
28 class Interface(Record):
29 """Wishbone interface.
30
31 See the `Wishbone specification <https://opencores.org/howto/wishbone>`_ for description
32 of the Wishbone signals. The ``RST_I`` and ``CLK_I`` signals are provided as a part of
33 the clock domain that drives the interface.
34
35 Note that the data width of the underlying memory map of the interface is equal to port
36 granularity, not port size. If port granularity is less than port size, then the address width
37 of the underlying memory map is extended to reflect that.
38
39 Parameters
40 ----------
41 addr_width : int
42 Width of the address signal.
43 data_width : int
44 Width of the data signals ("port size" in Wishbone terminology).
45 One of 8, 16, 32, 64.
46 granularity : int
47 Granularity of select signals ("port granularity" in Wishbone terminology).
48 One of 8, 16, 32, 64.
49 optional : iter(str)
50 Selects the optional signals that will be a part of this interface.
51 alignment : int
52 Resource and window alignment. See :class:`MemoryMap`.
53 name : str
54 Name of the underlying record.
55
56 Attributes
57 ----------
58 The correspondence between the nMigen-SoC signals and the Wishbone signals changes depending
59 on whether the interface acts as an initiator or a target.
60
61 adr : Signal(addr_width)
62 Corresponds to Wishbone signal ``ADR_O`` (initiator) or ``ADR_I`` (target).
63 dat_w : Signal(data_width)
64 Corresponds to Wishbone signal ``DAT_O`` (initiator) or ``DAT_I`` (target).
65 dat_r : Signal(data_width)
66 Corresponds to Wishbone signal ``DAT_I`` (initiator) or ``DAT_O`` (target).
67 sel : Signal(data_width // granularity)
68 Corresponds to Wishbone signal ``SEL_O`` (initiator) or ``SEL_I`` (target).
69 cyc : Signal()
70 Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I`` (target).
71 stb : Signal()
72 Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I`` (target).
73 we : Signal()
74 Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I`` (target).
75 ack : Signal()
76 Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O`` (target).
77 err : Signal()
78 Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator) or ``ERR_O`` (target).
79 rty : Signal()
80 Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator) or ``RTY_O`` (target).
81 stall : Signal()
82 Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator) or ``STALL_O`` (target).
83 cti : Signal()
84 Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator) or ``CTI_I`` (target).
85 bte : Signal()
86 Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator) or ``BTE_I`` (target).
87 """
88 def __init__(self, *, addr_width, data_width, granularity=None, optional=frozenset(),
89 alignment=0, name=None):
90 if not isinstance(addr_width, int) or addr_width < 0:
91 raise ValueError("Address width must be a non-negative integer, not {!r}"
92 .format(addr_width))
93 if data_width not in (8, 16, 32, 64):
94 raise ValueError("Data width must be one of 8, 16, 32, 64, not {!r}"
95 .format(data_width))
96 if granularity is None:
97 granularity = data_width
98 elif granularity not in (8, 16, 32, 64):
99 raise ValueError("Granularity must be one of 8, 16, 32, 64, not {!r}"
100 .format(granularity))
101 if granularity > data_width:
102 raise ValueError("Granularity {} may not be greater than data width {}"
103 .format(granularity, data_width))
104 self.addr_width = addr_width
105 self.data_width = data_width
106 self.granularity = granularity
107 granularity_bits = log2_int(data_width // granularity)
108 self.memory_map = MemoryMap(addr_width=max(1, addr_width + granularity_bits),
109 data_width=data_width >> granularity_bits,
110 alignment=alignment)
111
112 optional = set(optional)
113 unknown = optional - {"rty", "err", "stall", "cti", "bte"}
114 if unknown:
115 raise ValueError("Optional signal(s) {} are not supported"
116 .format(", ".join(map(repr, unknown))))
117 layout = [
118 ("adr", addr_width, Direction.FANOUT),
119 ("dat_w", data_width, Direction.FANOUT),
120 ("dat_r", data_width, Direction.FANIN),
121 ("sel", data_width // granularity, Direction.FANOUT),
122 ("cyc", 1, Direction.FANOUT),
123 ("stb", 1, Direction.FANOUT),
124 ("we", 1, Direction.FANOUT),
125 ("ack", 1, Direction.FANIN),
126 ]
127 if "err" in optional:
128 layout += [("err", 1, Direction.FANIN)]
129 if "rty" in optional:
130 layout += [("rty", 1, Direction.FANIN)]
131 if "stall" in optional:
132 layout += [("stall", 1, Direction.FANIN)]
133 if "cti" in optional:
134 layout += [("cti", CycleType, Direction.FANOUT)]
135 if "bte" in optional:
136 layout += [("bte", BurstTypeExt, Direction.FANOUT)]
137 super().__init__(layout, name=name, src_loc_at=1)