Skip to content

Commit

Permalink
Extract UART module into glasgow.gateware and add tests.
Browse files Browse the repository at this point in the history
This also fixes a few bugs and idiosyncrasies in it.
  • Loading branch information
whitequark committed Jul 23, 2018
1 parent 09b23d6 commit 0f497e4
Show file tree
Hide file tree
Showing 2 changed files with 465 additions and 126 deletions.
146 changes: 20 additions & 126 deletions software/glasgow/applet/uart.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,140 +4,33 @@
from migen.genlib.fsm import *

from . import GlasgowApplet
from ..gateware.pads import *
from ..gateware.uart import *


logger = logging.getLogger(__name__)


class UARTSubtarget(Module):
def __init__(self, rx, tx, out_fifo, in_fifo, baud_rate, max_deviation_ppm):
sys_freq = int(30e6)
bit_cyc = sys_freq // baud_rate
if bit_cyc <= 0:
raise ValueError("UART output frequency ({}) is too high"
.format(baud_rate))

actual_baud_rate = sys_freq // bit_cyc
deviation_ppm = 1000000 * (actual_baud_rate - baud_rate) / baud_rate
if deviation_ppm > max_deviation_ppm:
raise ValueError("UART output frequency deviation ({} ppm) is too high"
.format(deviation_ppm))

def __init__(self, pads, out_fifo, in_fifo, baud_rate, max_deviation):
bit_cyc, actual_baud_rate = uart_bit_cyc(30e6, baud_rate, max_deviation)
logger.debug("requested baud rate %d, actual %d", baud_rate, actual_baud_rate)

###

self.comb += tx.oe.eq(1)

rx_timer = Signal(max=bit_cyc)
rx_stb = Signal()
rx_bitno = Signal(3)
rx_data = Signal(8)

# TODO: make these readable via I2C
rx_ferrs = Signal(16)
rx_ovfs = Signal(16)

self.sync += [
If(rx_timer == 0,
rx_timer.eq(bit_cyc)
).Else(
rx_timer.eq(rx_timer - 1)
),
rx_stb.eq(rx_timer == 0)
]

self.submodules.rx_fsm = FSM(reset_state="IDLE")
self.rx_fsm.act("IDLE",
If(~rx.i,
NextValue(rx_timer, bit_cyc // 2),
NextState("START")
)
)
self.rx_fsm.act("START",
If(rx_stb,
NextState("DATA")
)
)
self.rx_fsm.act("DATA",
If(rx_stb,
NextValue(rx_data, Cat(rx_data[1:8], rx.i)),
NextValue(rx_bitno, rx_bitno + 1),
If(rx_bitno == 7,
NextState("STOP")
)
)
)
self.rx_fsm.act("STOP",
If(rx_stb,
If(~rx.i,
rx_ferrs.eq(rx_ferrs + 1),
NextState("IDLE")
).Else(
NextValue(in_fifo.din, rx_data),
NextState("DONE")
)
)
)
self.rx_fsm.act("DONE",
If(in_fifo.writable,
in_fifo.we.eq(1),
NextState("IDLE")
).Elif(~rx.i,
rx_ovfs.eq(rx_ovfs + 1),
NextState("IDLE")
)
)
self.submodules.uart = UART(pads, bit_cyc)

###

tx_timer = Signal(max=bit_cyc)
tx_stb = Signal()
tx_bitno = Signal(3)
tx_data = Signal(8)

self.sync += [
If(tx_timer == 0,
tx_timer.eq(bit_cyc)
).Else(
tx_timer.eq(tx_timer - 1)
),
tx_stb.eq(tx_timer == 0)
self.comb += [
in_fifo.din.eq(self.uart.rx_data),
in_fifo.we.eq(self.uart.rx_rdy),
self.uart.rx_ack.eq(in_fifo.writable)
]

self.submodules.tx_fsm = FSM(reset_state="IDLE")
self.tx_fsm.act("IDLE",
If(out_fifo.readable,
out_fifo.re.eq(1),
NextValue(tx_timer, bit_cyc - 1),
NextValue(tx_data, out_fifo.dout),
NextState("START")
).Else(
NextValue(tx.o, 1)
)
)
self.tx_fsm.act("START",
If(tx_stb,
NextValue(tx.o, 0),
NextState("DATA")
)
)
self.tx_fsm.act("DATA",
If(tx_stb,
NextValue(tx.o, tx_data[0]),
NextValue(tx_data, Cat(tx_data[1:8], 0)),
NextValue(tx_bitno, tx_bitno + 1),
If(tx_bitno == 7,
NextState("STOP")
)
)
)
self.tx_fsm.act("STOP",
If(tx_stb,
NextValue(tx.o, 1),
NextState("IDLE")
)
)
self.comb += [
self.uart.tx_data.eq(out_fifo.dout),
out_fifo.re.eq(self.uart.tx_rdy),
self.uart.tx_ack.eq(out_fifo.readable),
]


class UARTApplet(GlasgowApplet, name="uart"):
Expand All @@ -154,10 +47,11 @@ class UARTApplet(GlasgowApplet, name="uart"):
def add_build_arguments(cls, parser):
parser.add_argument(
"-b", "--baud-rate", metavar="BPS", type=int, default=115200,
help="set UART baud rate to BPS bits per second")
help="set UART baud rate to BPS bits per second (default: %(default)s)")
parser.add_argument(
"--max-deviation", metavar="PPM", type=int, default=50000,
help="verify that actual baud rate is within PPM parts per million of specified")
help="verify that actual baud rate is within PPM parts per million of specified"
" (default: %(default)s)")

cls.add_port_argument(parser, default="A")
cls.add_pin_argument(parser, "rx", default=0)
Expand All @@ -166,12 +60,12 @@ def add_build_arguments(cls, parser):
def build(self, target, args):
io_port = target.get_io_port(args.port)
target.submodules += UARTSubtarget(
rx=io_port[args.pin_rx],
tx=io_port[args.pin_tx],
pads=Pads(rx=io_port[args.pin_rx],
tx=io_port[args.pin_tx]),
out_fifo=target.get_out_fifo(args.port),
in_fifo=target.get_in_fifo(args.port, streaming=False),
baud_rate=args.baud_rate,
max_deviation_ppm=args.max_deviation,
max_deviation=args.max_deviation,
)

@classmethod
Expand Down

0 comments on commit 0f497e4

Please sign in to comment.