|
| 1 | +from amaranth import * |
| 2 | +from amaranth.lib import enum, wiring |
| 3 | +from amaranth.lib.wiring import In, Out, flipped, connect |
| 4 | + |
| 5 | +from . import csr |
| 6 | + |
| 7 | + |
| 8 | +__all__ = ["PinMode", "PinSignature", "Peripheral"] |
| 9 | + |
| 10 | + |
| 11 | +class PinMode(enum.Enum, shape=unsigned(2)): |
| 12 | + """GPIO pin mode. |
| 13 | +
|
| 14 | + The 2-bit values of this enumeration can be written to a :class:`Peripheral.Mode` field to |
| 15 | + configure the pins of a :class:`Peripheral`. |
| 16 | + """ |
| 17 | + |
| 18 | + #: `Input-only` mode. |
| 19 | + #: |
| 20 | + #: The pin output is disabled but remains connected to its :class:`Peripheral.Output` field. |
| 21 | + #: Its :attr:`Peripheral.alt_mode` bit is wired to 0. |
| 22 | + INPUT_ONLY = 0b00 |
| 23 | + |
| 24 | + #: `Push-pull` mode. |
| 25 | + #: |
| 26 | + #: The pin output is enabled and connected to its :class:`Peripheral.Output` field. Its |
| 27 | + #: :attr:`Peripheral.alt_mode` bit is wired to 0. |
| 28 | + PUSH_PULL = 0b01 |
| 29 | + |
| 30 | + #: `Open-drain` mode. |
| 31 | + #: |
| 32 | + #: The pin output is enabled when the value of its :class:`Peripheral.Output` field is 0, and |
| 33 | + #: is itself wired to 0. Its :attr:`Peripheral.alt_mode` bit is wired to 0. |
| 34 | + OPEN_DRAIN = 0b10 |
| 35 | + |
| 36 | + #: `Alternate` mode. |
| 37 | + #: |
| 38 | + #: The pin output is disabled but remains connected to its :class:`Peripheral.Output` field. |
| 39 | + #: Its :attr:`Peripheral.alt_mode` bit is wired to 1. |
| 40 | + ALTERNATE = 0b11 |
| 41 | + |
| 42 | + |
| 43 | +class PinSignature(wiring.Signature): |
| 44 | + """GPIO pin signature. |
| 45 | +
|
| 46 | + Interface attributes |
| 47 | + -------------------- |
| 48 | + i : :class:`Signal` |
| 49 | + Input. |
| 50 | + o : :class:`Signal` |
| 51 | + Output. |
| 52 | + oe : :class:`Signal` |
| 53 | + Output enable. |
| 54 | + """ |
| 55 | + def __init__(self): |
| 56 | + super().__init__({ |
| 57 | + "i": In(unsigned(1)), |
| 58 | + "o": Out(unsigned(1)), |
| 59 | + "oe": Out(unsigned(1)), |
| 60 | + }) |
| 61 | + |
| 62 | + |
| 63 | +class Peripheral(wiring.Component): |
| 64 | + class Mode(csr.Register, access="rw"): |
| 65 | + """Mode register. |
| 66 | +
|
| 67 | + This :class:`csr.Register` contains an array of ``pin_count`` read/write fields. Each field |
| 68 | + is 2-bit wide and its possible values are defined by the :class:`PinMode` enumeration. |
| 69 | +
|
| 70 | + If ``pin_count`` is 8, then the register has the following fields: |
| 71 | +
|
| 72 | + .. bitfield:: |
| 73 | + :bits: 16 |
| 74 | +
|
| 75 | + [ |
| 76 | + { "name": "pin[0]", "bits": 2, "attr": "RW" }, |
| 77 | + { "name": "pin[1]", "bits": 2, "attr": "RW" }, |
| 78 | + { "name": "pin[2]", "bits": 2, "attr": "RW" }, |
| 79 | + { "name": "pin[3]", "bits": 2, "attr": "RW" }, |
| 80 | + { "name": "pin[4]", "bits": 2, "attr": "RW" }, |
| 81 | + { "name": "pin[5]", "bits": 2, "attr": "RW" }, |
| 82 | + { "name": "pin[6]", "bits": 2, "attr": "RW" }, |
| 83 | + { "name": "pin[7]", "bits": 2, "attr": "RW" }, |
| 84 | + ] |
| 85 | +
|
| 86 | + Parameters |
| 87 | + ---------- |
| 88 | + pin_count : :class:`int` |
| 89 | + Number of GPIO pins. |
| 90 | + """ |
| 91 | + def __init__(self, pin_count): |
| 92 | + super().__init__({ |
| 93 | + "pin": [csr.Field(csr.action.RW, PinMode) for _ in range(pin_count)], |
| 94 | + }) |
| 95 | + |
| 96 | + class Input(csr.Register, access="r"): |
| 97 | + """Input register. |
| 98 | +
|
| 99 | + This :class:`csr.Register` contains an array of ``pin_count`` read-only fields. Each field |
| 100 | + is 1-bit wide and driven by the input of its associated pin in the :attr:`Peripheral.pins` |
| 101 | + array. |
| 102 | +
|
| 103 | + Values sampled from pin inputs go through :attr:`Peripheral.input_stages` synchronization |
| 104 | + stages (on a rising edge of ``ClockSignal("sync")``) before reaching the register. |
| 105 | +
|
| 106 | + If ``pin_count`` is 8, then the register has the following fields: |
| 107 | +
|
| 108 | + .. bitfield:: |
| 109 | + :bits: 8 |
| 110 | +
|
| 111 | + [ |
| 112 | + { "name": "pin[0]", "bits": 1, "attr": "R" }, |
| 113 | + { "name": "pin[1]", "bits": 1, "attr": "R" }, |
| 114 | + { "name": "pin[2]", "bits": 1, "attr": "R" }, |
| 115 | + { "name": "pin[3]", "bits": 1, "attr": "R" }, |
| 116 | + { "name": "pin[4]", "bits": 1, "attr": "R" }, |
| 117 | + { "name": "pin[5]", "bits": 1, "attr": "R" }, |
| 118 | + { "name": "pin[6]", "bits": 1, "attr": "R" }, |
| 119 | + { "name": "pin[7]", "bits": 1, "attr": "R" }, |
| 120 | + ] |
| 121 | +
|
| 122 | + Parameters |
| 123 | + ---------- |
| 124 | + pin_count : :class:`int` |
| 125 | + Number of GPIO pins. |
| 126 | + """ |
| 127 | + def __init__(self, pin_count): |
| 128 | + super().__init__({ |
| 129 | + "pin": [csr.Field(csr.action.R, unsigned(1)) for _ in range(pin_count)], |
| 130 | + }) |
| 131 | + |
| 132 | + class Output(csr.Register, access="rw"): |
| 133 | + """Output register. |
| 134 | +
|
| 135 | + This :class:`csr.Register` contains an array of ``pin_count`` read/write fields. Each field |
| 136 | + is 1-bit wide and drives the output of its associated pin in the :attr:`Peripheral.pins` |
| 137 | + array, depending on its associated :class:`~Peripheral.Mode` field. |
| 138 | +
|
| 139 | + If ``pin_count`` is 8, then the register has the following fields: |
| 140 | +
|
| 141 | + .. bitfield:: |
| 142 | + :bits: 8 |
| 143 | +
|
| 144 | + [ |
| 145 | + { "name": "pin[0]", "bits": 1, "attr": "RW" }, |
| 146 | + { "name": "pin[1]", "bits": 1, "attr": "RW" }, |
| 147 | + { "name": "pin[2]", "bits": 1, "attr": "RW" }, |
| 148 | + { "name": "pin[3]", "bits": 1, "attr": "RW" }, |
| 149 | + { "name": "pin[4]", "bits": 1, "attr": "RW" }, |
| 150 | + { "name": "pin[5]", "bits": 1, "attr": "RW" }, |
| 151 | + { "name": "pin[6]", "bits": 1, "attr": "RW" }, |
| 152 | + { "name": "pin[7]", "bits": 1, "attr": "RW" }, |
| 153 | + ] |
| 154 | +
|
| 155 | + Parameters |
| 156 | + ---------- |
| 157 | + pin_count : :class:`int` |
| 158 | + Number of GPIO pins. |
| 159 | + """ |
| 160 | + class _FieldAction(csr.FieldAction): |
| 161 | + def __init__(self): |
| 162 | + super().__init__(shape=unsigned(1), access="rw", members=( |
| 163 | + ("data", Out(unsigned(1))), |
| 164 | + ("set", In(unsigned(1))), |
| 165 | + ("clr", In(unsigned(1))), |
| 166 | + )) |
| 167 | + self._storage = Signal(unsigned(1)) |
| 168 | + |
| 169 | + def elaborate(self, platform): |
| 170 | + m = Module() |
| 171 | + |
| 172 | + with m.If(self.set != self.clr): |
| 173 | + m.d.sync += self._storage.eq(self.set) |
| 174 | + with m.Elif(self.port.w_stb): |
| 175 | + m.d.sync += self._storage.eq(self.port.w_data) |
| 176 | + |
| 177 | + m.d.comb += [ |
| 178 | + self.port.r_data.eq(self._storage), |
| 179 | + self.data.eq(self._storage), |
| 180 | + ] |
| 181 | + |
| 182 | + return m |
| 183 | + |
| 184 | + def __init__(self, pin_count): |
| 185 | + super().__init__({ |
| 186 | + "pin": [csr.Field(self._FieldAction) for _ in range(pin_count)], |
| 187 | + }) |
| 188 | + |
| 189 | + class SetClr(csr.Register, access="w"): |
| 190 | + """Output set/clear register. |
| 191 | +
|
| 192 | + This :class:`csr.Register` contains an array of ``pin_count`` write-only fields. Each field |
| 193 | + is 2-bit wide; writing it can modify its associated :class:`~Peripheral.Output` field as a |
| 194 | + side-effect. |
| 195 | +
|
| 196 | + If ``pin_count`` is 8, then the register has the following fields: |
| 197 | +
|
| 198 | + .. bitfield:: |
| 199 | + :bits: 16 |
| 200 | +
|
| 201 | + [ |
| 202 | + { "name": "pin[0]", "bits": 2, "attr": "W" }, |
| 203 | + { "name": "pin[1]", "bits": 2, "attr": "W" }, |
| 204 | + { "name": "pin[2]", "bits": 2, "attr": "W" }, |
| 205 | + { "name": "pin[3]", "bits": 2, "attr": "W" }, |
| 206 | + { "name": "pin[4]", "bits": 2, "attr": "W" }, |
| 207 | + { "name": "pin[5]", "bits": 2, "attr": "W" }, |
| 208 | + { "name": "pin[6]", "bits": 2, "attr": "W" }, |
| 209 | + { "name": "pin[7]", "bits": 2, "attr": "W" }, |
| 210 | + ] |
| 211 | +
|
| 212 | + - Writing `0b01` to a field sets its associated :class:`~Peripheral.Output` field. |
| 213 | + - Writing `0b10` to a field clears its associated :class:`~Peripheral.Output` field. |
| 214 | + - Writing `0b00` or `0b11` to a field has no side-effect. |
| 215 | +
|
| 216 | + Parameters |
| 217 | + ---------- |
| 218 | + pin_count : :class:`int` |
| 219 | + Number of GPIO pins. |
| 220 | + """ |
| 221 | + def __init__(self, pin_count): |
| 222 | + pin_fields = { |
| 223 | + "set": csr.Field(csr.action.W, unsigned(1)), |
| 224 | + "clr": csr.Field(csr.action.W, unsigned(1)), |
| 225 | + } |
| 226 | + super().__init__({ |
| 227 | + "pin": [pin_fields for _ in range(pin_count)], |
| 228 | + }) |
| 229 | + |
| 230 | + """GPIO peripheral. |
| 231 | +
|
| 232 | + Parameters |
| 233 | + ---------- |
| 234 | + pin_count : :class:`int` |
| 235 | + Number of GPIO pins. |
| 236 | + addr_width : :class:`int` |
| 237 | + CSR bus address width. |
| 238 | + data_width : :class:`int` |
| 239 | + CSR bus data width. |
| 240 | + name : :class:`str` |
| 241 | + CSR bus window name. Optional. |
| 242 | + input_stages : :class:`int` |
| 243 | + Number of synchronization stages between pin inputs and the :class:`~Peripheral.Input` |
| 244 | + register. Optional. Defaults to ``2``. |
| 245 | +
|
| 246 | + Attributes |
| 247 | + ---------- |
| 248 | + bus : :class:`csr.Interface` |
| 249 | + CSR bus interface providing access to registers. |
| 250 | + pins : :class:`list` of :class:`wiring.PureInterface` of :class:`PinSignature` |
| 251 | + GPIO pin interfaces. |
| 252 | + alt_mode : :class:`Signal` |
| 253 | + Indicates which members of the :attr:`Peripheral.pins` array are in alternate mode. |
| 254 | +
|
| 255 | + Raises |
| 256 | + ------ |
| 257 | + :exc:`TypeError` |
| 258 | + If ``pin_count`` is not a positive integer. |
| 259 | + :exc:`TypeError` |
| 260 | + If ``input_stages`` is not a non-negative integer. |
| 261 | + """ |
| 262 | + def __init__(self, *, pin_count, addr_width, data_width, name=None, input_stages=2): |
| 263 | + if not isinstance(pin_count, int) or pin_count <= 0: |
| 264 | + raise TypeError(f"Pin count must be a positive integer, not {pin_count!r}") |
| 265 | + if not isinstance(input_stages, int) or input_stages < 0: |
| 266 | + raise TypeError(f"Input stages must be a non-negative integer, not {input_stages!r}") |
| 267 | + |
| 268 | + regs = csr.Builder(addr_width=addr_width, data_width=data_width, name=name) |
| 269 | + |
| 270 | + self._mode = regs.add("Mode", self.Mode(pin_count)) |
| 271 | + self._input = regs.add("Input", self.Input(pin_count)) |
| 272 | + self._output = regs.add("Output", self.Output(pin_count)) |
| 273 | + self._setclr = regs.add("SetClr", self.SetClr(pin_count)) |
| 274 | + |
| 275 | + self._bridge = csr.Bridge(regs.as_memory_map()) |
| 276 | + |
| 277 | + super().__init__({ |
| 278 | + "bus": In(csr.Signature(addr_width=addr_width, data_width=data_width)), |
| 279 | + "pins": Out(PinSignature()).array(pin_count), |
| 280 | + "alt_mode": Out(unsigned(pin_count)), |
| 281 | + }) |
| 282 | + self.bus.memory_map = self._bridge.bus.memory_map |
| 283 | + |
| 284 | + self._pin_count = pin_count |
| 285 | + self._input_stages = input_stages |
| 286 | + |
| 287 | + @property |
| 288 | + def pin_count(self): |
| 289 | + """Number of GPIO pins. |
| 290 | +
|
| 291 | + Returns |
| 292 | + ------- |
| 293 | + :class:`int` |
| 294 | + """ |
| 295 | + return self._pin_count |
| 296 | + |
| 297 | + @property |
| 298 | + def input_stages(self): |
| 299 | + """Number of synchronization stages between pin inputs and the :class:`~Peripheral.Input` |
| 300 | + register. |
| 301 | +
|
| 302 | + Returns |
| 303 | + ------- |
| 304 | + :class:`int` |
| 305 | + """ |
| 306 | + return self._input_stages |
| 307 | + |
| 308 | + def elaborate(self, platform): |
| 309 | + m = Module() |
| 310 | + m.submodules.bridge = self._bridge |
| 311 | + |
| 312 | + connect(m, flipped(self.bus), self._bridge.bus) |
| 313 | + |
| 314 | + for n, pin in enumerate(self.pins): |
| 315 | + pin_i_sync = pin.i |
| 316 | + for stage in range(self.input_stages): |
| 317 | + pin_i_sync_ff = Signal(reset_less=True, name=f"pin_{n}_i_sync_ff_{stage}") |
| 318 | + m.d.sync += pin_i_sync_ff.eq(pin_i_sync) |
| 319 | + pin_i_sync = pin_i_sync_ff |
| 320 | + del pin_i_sync_ff |
| 321 | + |
| 322 | + m.d.comb += self._input.f.pin[n].r_data.eq(pin_i_sync) |
| 323 | + |
| 324 | + with m.If(self._setclr.f.pin[n].set.w_stb & self._setclr.f.pin[n].set.w_data): |
| 325 | + m.d.comb += self._output.f.pin[n].set.eq(1) |
| 326 | + with m.If(self._setclr.f.pin[n].clr.w_stb & self._setclr.f.pin[n].clr.w_data): |
| 327 | + m.d.comb += self._output.f.pin[n].clr.eq(1) |
| 328 | + |
| 329 | + with m.Switch(self._mode.f.pin[n].data): |
| 330 | + with m.Case(PinMode.INPUT_ONLY): |
| 331 | + m.d.comb += [ |
| 332 | + pin.o .eq(self._output.f.pin[n].data), |
| 333 | + pin.oe.eq(0), |
| 334 | + ] |
| 335 | + with m.Case(PinMode.PUSH_PULL): |
| 336 | + m.d.comb += [ |
| 337 | + pin.o .eq(self._output.f.pin[n].data), |
| 338 | + pin.oe.eq(1), |
| 339 | + ] |
| 340 | + with m.Case(PinMode.OPEN_DRAIN): |
| 341 | + m.d.comb += [ |
| 342 | + pin.o .eq(0), |
| 343 | + pin.oe.eq(~self._output.f.pin[n].data), |
| 344 | + ] |
| 345 | + with m.Case(PinMode.ALTERNATE): |
| 346 | + m.d.comb += [ |
| 347 | + pin.o .eq(self._output.f.pin[n].data), |
| 348 | + pin.oe.eq(0), |
| 349 | + ] |
| 350 | + m.d.comb += self.alt_mode[n].eq(1) |
| 351 | + |
| 352 | + return m |
0 commit comments