examples: add concise UART example.

This example uses shift registers and counters instead of an explicit
FSM, which makes it very compact in terms of generated logic, and
more concise too.
This commit is contained in:
whitequark 2019-06-27 04:51:45 +00:00
parent 6f4e3156d8
commit 2b92f12016

142
examples/basic/uart.py Normal file
View file

@ -0,0 +1,142 @@
from nmigen import *
class UART(Elaboratable):
def __init__(self, divisor, data_bits=8):
assert divisor >= 4
self.data_bits = data_bits
self.divisor = divisor
self.tx_o = Signal()
self.rx_i = Signal()
self.tx_data = Signal(data_bits)
self.tx_rdy = Signal()
self.tx_ack = Signal()
self.rx_data = Signal(data_bits)
self.rx_err = Signal()
self.rx_ovf = Signal()
self.rx_rdy = Signal()
self.rx_ack = Signal()
def elaborate(self, platform):
m = Module()
tx_phase = Signal(max=self.divisor)
tx_shreg = Signal(1 + self.data_bits + 1, reset=-1)
tx_count = Signal(max=len(tx_shreg) + 1)
m.d.comb += self.tx_o.eq(tx_shreg[0])
with m.If(tx_count == 0):
m.d.comb += self.tx_ack.eq(1)
with m.If(self.tx_rdy):
m.d.sync += [
tx_shreg.eq(Cat(C(0, 1), self.tx_data, C(1, 1))),
tx_count.eq(len(tx_shreg)),
tx_phase.eq(self.divisor - 1),
]
with m.Else():
with m.If(tx_phase != 0):
m.d.sync += tx_phase.eq(tx_phase - 1)
with m.Else():
m.d.sync += [
tx_shreg.eq(Cat(tx_shreg[1:], C(1, 1))),
tx_count.eq(tx_count - 1),
tx_phase.eq(self.divisor - 1),
]
rx_phase = Signal(max=self.divisor)
rx_shreg = Signal(1 + self.data_bits + 1, reset=-1)
rx_count = Signal(max=len(rx_shreg) + 1)
m.d.comb += self.rx_data.eq(rx_shreg[1:-1])
with m.If(rx_count == 0):
m.d.comb += self.rx_err.eq(~(~rx_shreg[0] & rx_shreg[-1]))
with m.If(~self.rx_i):
with m.If(self.rx_ack | ~self.rx_rdy):
m.d.sync += [
self.rx_rdy.eq(0),
self.rx_ovf.eq(0),
rx_count.eq(len(rx_shreg)),
rx_phase.eq(self.divisor // 2),
]
with m.Else():
m.d.sync += self.rx_ovf.eq(1)
with m.Else():
with m.If(rx_phase != 0):
m.d.sync += rx_phase.eq(rx_phase - 1)
with m.Else():
m.d.sync += [
rx_shreg.eq(Cat(rx_shreg[1:], self.rx_i)),
rx_count.eq(rx_count - 1),
rx_phase.eq(self.divisor - 1),
]
with m.If(rx_count == 1):
m.d.sync += self.rx_rdy.eq(1)
return m
if __name__ == "__main__":
uart = UART(divisor=5)
ports = [
uart.tx_o, uart.rx_i,
uart.tx_data, uart.tx_rdy, uart.tx_ack,
uart.rx_data, uart.rx_rdy, uart.rx_err, uart.rx_ovf, uart.rx_ack
]
import argparse
parser = argparse.ArgumentParser()
p_action = parser.add_subparsers(dest="action")
p_action.add_parser("simulate")
p_action.add_parser("generate")
args = parser.parse_args()
if args.action == "simulate":
from nmigen.hdl.ast import Passive
from nmigen.back import pysim
with pysim.Simulator(uart,
vcd_file=open("uart.vcd", "w"),
gtkw_file=open("uart.gtkw", "w"),
traces=ports) as sim:
sim.add_clock(1e-6)
def loopback_proc():
yield Passive()
while True:
yield uart.rx_i.eq((yield uart.tx_o))
yield
sim.add_sync_process(loopback_proc())
def transmit_proc():
assert (yield uart.tx_ack)
assert not (yield uart.rx_rdy)
yield uart.tx_data.eq(0x5A)
yield uart.tx_rdy.eq(1)
yield
yield uart.tx_rdy.eq(0)
yield
assert not (yield uart.tx_ack)
for _ in range(uart.divisor * 12): yield
assert (yield uart.tx_ack)
assert (yield uart.rx_rdy)
assert not (yield uart.rx_err)
assert (yield uart.rx_data) == 0x5A
yield uart.rx_ack.eq(1)
yield
sim.add_sync_process(transmit_proc())
sim.run()
if args.action == "generate":
from nmigen.back import verilog
print(verilog.convert(uart, ports=ports))