Skip to content

Instantly share code, notes, and snippets.

@eddyb
Last active May 3, 2020 07:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eddyb/e3afeb490874f110b75f04cbd20dc20b to your computer and use it in GitHub Desktop.
Save eddyb/e3afeb490874f110b75f04cbd20dc20b to your computer and use it in GitHub Desktop.
from nmigen import *
from nmigen.build import ResourceError
from nmigen_boards.icestick import *
from uart import *
class AoC2019_1_1(Elaboratable):
def __init__(self, digits=7, baud_rate=115200):
self.digits = digits
self.baud_rate = baud_rate
def elaborate(self, platform):
m = Module()
leds = Cat(platform.request("led", i).o for i in range(5))
err = Signal(range(4), reset=0)
with m.If(err != 0):
m.d.sync += leds.eq((leds & ~0b10000) | (1 << err))
with m.Else():
m.d.sync += leds.eq(0b10000)
uart_res = platform.request("uart", 0)
uart = m.submodules.uart = UART(
round(platform.default_clk_frequency / self.baud_rate),
)
uart.tx_rdy.reset = 0
m.d.comb += [
uart.rx_i.eq(uart_res.rx),
uart_res.tx.eq(uart.tx_o),
]
in_val = Signal(range(10**self.digits), reset=0)
out_en = Signal(reset=0)
out_val = Signal(range(10**self.digits), reset=0)
out_digit = Signal(range(10))
out_idx = Signal(range(self.digits))
# Input loop.
with m.If(uart.rx_rdy):
c = uart.rx_data
m.d.sync += uart.rx_ack.eq(1)
with m.If(~uart.rx_ack):
with m.If(c == ord('R') - 0x40): # ^R (Ctrl-R)
# FIXME(eddyb) maybe trigger a full reset?
m.d.sync += [
err.eq(err.reset),
in_val.eq(in_val.reset),
out_en.eq(out_en.reset),
out_val.eq(out_val.reset),
]
with m.Elif((c >= ord('0')) & (c <= ord('9'))):
m.d.sync += [
in_val.eq(in_val * 10 + (c - ord('0'))),
]
with m.Else():
with m.If(out_en):
m.d.sync += err.eq(1)
with m.Else():
m.d.sync += [
in_val.eq(0),
out_val.eq(out_val + in_val // 3 - 2),
]
with m.If(c == ord(' ')):
m.d.sync += []
with m.Elif(c == ord('\n')):
m.d.sync += [
out_en.eq(1),
out_digit.eq(0),
out_idx.eq(self.digits - 1),
]
with m.Else():
m.d.sync += err.eq(2)
with m.Else():
m.d.sync += uart.rx_ack.eq(0)
# Output loop.
with m.If(out_en):
# Cheap Mux.
#pow10 = C(0, 32);
#for i in range(10):
# pow10 = Mux(out_idx == i, 10**i, pow10)
# Expensive Array.
pow10 = Array(10**i for i in range(self.digits))[out_idx]
with m.If(out_val >= pow10):
m.d.sync += [
out_digit.eq(out_digit + 1),
out_val.eq(out_val - pow10),
]
with m.Elif(~uart.tx_rdy):
m.d.sync += [
uart.tx_rdy.eq(1),
uart.tx_data.eq(ord('0') + out_digit),
out_digit.eq(0),
]
with m.If(out_idx != 0):
m.d.sync += out_idx.eq(out_idx - 1)
with m.Else():
m.d.sync += out_en.eq(0)
with m.If(uart.tx_rdy & uart.tx_ack):
m.d.sync += uart.tx_rdy.eq(0)
return m
if __name__ == "__main__":
ICEStickPlatform().build(AoC2019_1_1(), do_program=True)
from nmigen import *
from nmigen.build import ResourceError
from nmigen_boards.icestick import *
from uart import *
class DecSum(Elaboratable):
def __init__(self, digits=7, baud_rate=115200):
self.digits = digits
self.baud_rate = baud_rate
def elaborate(self, platform):
m = Module()
leds = Cat(platform.request("led", i).o for i in range(5))
err = Signal(range(4), reset=0)
with m.If(err != 0):
m.d.sync += leds.eq((leds & ~0b10000) | (1 << err))
with m.Else():
m.d.sync += leds.eq(0b10000)
uart_res = platform.request("uart", 0)
uart = m.submodules.uart = UART(
round(platform.default_clk_frequency / self.baud_rate),
)
uart.tx_rdy.reset = 0
m.d.comb += [
uart.rx_i.eq(uart_res.rx),
uart_res.tx.eq(uart.tx_o),
]
in_val = Signal(range(10**self.digits), reset=0)
out_en = Signal(reset=0)
out_val = Signal(range(10**self.digits), reset=0)
out_digit = Signal(range(10))
out_idx = Signal(range(self.digits))
# Input loop.
with m.If(uart.rx_rdy):
c = uart.rx_data
m.d.sync += uart.rx_ack.eq(1)
with m.If(~uart.rx_ack):
with m.If(c == ord('R') - 0x40): # ^R (Ctrl-R)
# FIXME(eddyb) maybe trigger a full reset?
m.d.sync += [
err.eq(err.reset),
in_val.eq(in_val.reset),
out_en.eq(out_en.reset),
out_val.eq(out_val.reset),
]
with m.Elif((c >= ord('0')) & (c <= ord('9'))):
m.d.sync += [
in_val.eq(in_val * 10 + (c - ord('0'))),
]
with m.Else():
with m.If(out_en):
m.d.sync += err.eq(1)
with m.Else():
m.d.sync += [
in_val.eq(0),
out_val.eq(out_val + in_val),
]
with m.If(c == ord(' ')):
m.d.sync += []
with m.Elif(c == ord('\n')):
m.d.sync += [
out_en.eq(1),
out_digit.eq(0),
out_idx.eq(self.digits - 1),
]
with m.Else():
m.d.sync += err.eq(2)
with m.Else():
m.d.sync += uart.rx_ack.eq(0)
# Output loop.
with m.If(out_en):
pow10 = Array(10**i for i in range(self.digits))
with m.If(out_val >= pow10[out_idx]):
m.d.sync += [
out_digit.eq(out_digit + 1),
out_val.eq(out_val - pow10[out_idx]),
]
with m.Elif(~uart.tx_rdy):
m.d.sync += [
uart.tx_rdy.eq(1),
uart.tx_data.eq(ord('0') + out_digit),
out_digit.eq(0),
]
with m.If(out_idx != 0):
m.d.sync += out_idx.eq(out_idx - 1)
with m.Else():
m.d.sync += out_en.eq(0)
with m.If(uart.tx_rdy & uart.tx_ack):
m.d.sync += uart.tx_rdy.eq(0)
return m
if __name__ == "__main__":
ICEStickPlatform().build(DecSum(), do_program=True)
with import <nixpkgs> {};
mkShell {
propagatedBuildInputs = [
yosys
nextpnr
icestorm
(python37.withPackages (pkgs: with pkgs; [
(buildPythonPackage {
pname = "nmigen-boards";
version = "0.1";
src = fetchgit {
url = "https://github.com/m-labs/nmigen-boards.git";
rev = "d70a8d6a60ecd5b4c9d604d57c010718d1f1f1fa";
sha256 = "05y3dq084z1hs51s28k1fh92pwxgrfcjppmazs3d51snpln412kz";
leaveDotGit = true;
};
nativeBuildInputs = [ setuptools_scm git ];
propagatedBuildInputs = [
(buildPythonPackage {
pname = "nmigen";
version = "0.1";
src = fetchgit {
url = "https://github.com/m-labs/nmigen.git";
rev = "67650214b703fee8d044fd1f165c513cc5b38964";
sha256 = "0g6kcsa0lz6lfihcc0g2lj74bpnfg8p2hz3ap74vj2a780k5svd9";
leaveDotGit = true;
};
doCheck = false;
nativeBuildInputs = [ setuptools_scm git ];
propagatedBuildInputs = [ bitarray jinja2 pyvcd setuptools ];
})
];
})
]))
];
}
import itertools
from nmigen import *
# https://github.com/m-labs/nmigen/blob/67650214b703fee8d044fd1f165c513cc5b38964/examples/basic/uart.py
class UART(Elaboratable):
"""
Parameters
----------
divisor : int
Set to ``round(clk-rate / baud-rate)``.
E.g. ``12e6 / 115200`` = ``104``.
"""
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(range(self.divisor))
tx_shreg = Signal(1 + self.data_bits + 1, reset=-1)
tx_count = Signal(range(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(range(self.divisor))
rx_shreg = Signal(1 + self.data_bits + 1, reset=-1)
rx_count = Signal(range(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
@eddyb
Copy link
Author

eddyb commented Dec 7, 2019

NMIGEN_synth_opts=-abc9 python hello_world.py

stty -crtscts raw 115200 -F /dev/ttyUSB1
cat /dev/ttyUSB1
NMIGEN_synth_opts=-abc9 python dec.py

# Quick example (should output 3456789).
stty -crtscts raw 115200 -F /dev/ttyUSB1
cat /dev/ttyUSB1&
echo 1111111 2345678 > /dev/ttyUSB1
fg

# Full console, ^A^X (Ctrl-A Ctrl-X) to exit.
picocom -b 115200 /dev/ttyUSB1 -qc --omap crlf --imap lfcrlf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment