Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/nmigen-stdio
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: df43d0d3b068
Choose a base ref
...
head repository: m-labs/nmigen-stdio
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: b295c9813406
Choose a head ref
  • 1 commit
  • 1 file changed
  • 1 contributor

Commits on Sep 20, 2019

  1. serial: WIP

    whitequark committed Sep 20, 2019
    Copy the full SHA
    b295c98 View commit details
Showing with 178 additions and 0 deletions.
  1. +178 −0 nmigen_stdio/serial.py
178 changes: 178 additions & 0 deletions nmigen_stdio/serial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
from nmigen import *
from nmigen.lib.cdc import MultiReg
from nmigen.tools import bits_for


__all__ = ["AsyncSerialRX", "AsyncSerialTX", "AsyncSerial"]


def _check_parity(parity):
choices = ("none", "even", "odd", "mark", "space")
if parity not in choices:
raise ValueError("Invalid parity {!r}; must be one of {}"
.format(parity, ", ".join(choices)))


def _compute_parity_bit(data, parity):
if parity == "none":
return C(0, 0)
if parity == "even":
return data.xor()
if parity == "odd":
return ~data.xor()
if parity == "mark":
return C(1, 1)
if parity == "space":
return C(0, 1)


def _wire_layout(data_bits, parity="none"):
return [
("start", 1),
("data", data_bits),
("parity", 0 if parity == "none" else 1),
("stop", 1),
]


class AsyncSerialRX(Elaboratable):
def __init__(self, *, divisor, divisor_bits=None, data_bits=8, parity="none", pins=None):
_check_parity(parity)

self.divisor = Signal(divisor_bits or bits_for(divisor), reset=divisor)
self._parity = parity

self.data = Signal(data_bits)
self.err = Record([
("overflow", 1),
("frame", 1),
])
self.rdy = Signal()
self.ack = Signal()

self.i = Signal(reset=1)

self._pins = pins

def elaborate(self, platform):
m = Module()

timer = Signal.like(self.divisor)
shreg = Record(_wire_layout(len(self.data), self._parity))
bitno = Signal(max=len(shreg))

if self._pins is not None:
m.d.submodules += MultiReg(self._pins.rx.i, self.i, reset=1)

with m.FSM() as fsm:
with m.State("IDLE"):
with m.If(~self.i):
m.d.sync += [
bitno.eq(len(shreg) - 1),
timer.eq(self.divisor >> 1),
]
m.next = "BUSY"

with m.State("BUSY"):
with m.If(timer != 0):
m.d.sync += timer.eq(timer - 1)
with m.Else():
m.d.sync += [
shreg.eq(Cat(self.i, shreg)),
bitno.eq(bitno - 1),
timer.eq(self.divisor),
]
with m.If(bitno == 0):
m.next = "DONE"

with m.State("DONE"):
with m.If(self.ack):
m.d.sync += [
self.data.eq(shreg.data),
self.err.frame.eq(~(
(shreg.start == 0) &
(shreg.stop == ~0) &
(shreg.parity == _compute_parity_bit(shreg.data, self._parity))
)),
]
m.d.sync += self.err.overflow.eq(~self.ack)

with m.If(fsm.ongoing("DONE")):
m.d.sync += self.rdy.eq(1)
with m.Elif(self.ack):
m.d.sync += self.rdy.eq(0)

return m


class AsyncSerialTX(Elaboratable):
def __init__(self, *, divisor, divisor_bits=None, data_bits=8, parity="none", pins=None):
_check_parity(parity)

self.divisor = Signal(divisor_bits or bits_for(divisor), reset=divisor)
self._parity = parity

self.data = Signal(data_bits)
self.rdy = Signal()
self.ack = Signal()

self.o = Signal()

self._pins = pins

def elaborate(self, platform):
m = Module()

timer = Signal.like(self.divisor)
shreg = Record(_wire_layout(len(self.data), self._parity))
bitno = Signal(max=len(shreg))

if self._pins is not None:
m.d.comb += self._pins.tx.o.eq(self.o)

with m.FSM():
with m.State("IDLE"):
m.d.comb += self.rdy.eq(1)
m.d.sync += self.o.eq(shreg[0])
with m.If(self.ack):
m.d.sync += [
shreg.start .eq(0),
shreg.data .eq(self.data),
shreg.parity.eq(_compute_parity_bit(self.data, self._parity)),
shreg.stop .eq(~0),
bitno.eq(len(shreg) - 1),
timer.eq(self.divisor),
]
m.next = "BUSY"

with m.State("BUSY"):
with m.If(timer != 0):
m.d.sync += timer.eq(timer - 1)
with m.Else():
m.d.sync += [
Cat(self.o, shreg).eq(shreg),
bitno.eq(bitno - 1),
timer.eq(self.divisor),
]
with m.If(bitno == 0):
m.next = "IDLE"

return m


class AsyncSerial(Elaboratable):
def __init__(self, *, divisor, divisor_bits=None, **kwargs):
self.divisor = Signal(divisor_bits or bits_for(divisor), reset=divisor)

self.rx = AsyncSerialRX(**kwargs)
self.tx = AsyncSerialTX(**kwargs)

def elaborate(self, platform):
m = Module()
m.submodules.rx = self.rx
m.submodules.tx = self.tx
m.d.comb += [
self.rx.divisor.eq(self.divisor),
self.tx.divisor.eq(self.divisor),
]
return m