Compare commits
5 Commits
d84b172245
...
7ffce5882e
Author | SHA1 | Date |
---|---|---|
Sebastien Bourdeauducq | 7ffce5882e | |
Sebastien Bourdeauducq | 5bc9189709 | |
Sebastien Bourdeauducq | 70638e6d87 | |
Sebastien Bourdeauducq | 88db84cfd7 | |
Sebastien Bourdeauducq | d765dfb7b9 |
|
@ -0,0 +1,26 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {}
|
||||||
|
, hx ? import ../default.nix { inherit pkgs; }}:
|
||||||
|
|
||||||
|
let
|
||||||
|
symbiflowInput = pkgs.runCommand "simplesoc-symbiflow-input" {
|
||||||
|
buildInputs = [ (pkgs.python3.withPackages(ps: [hx.nmigen hx.heavycomps hx.minerva])) hx.yosys ];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
mkdir $out
|
||||||
|
|
||||||
|
python ${./simplesoc_ecp5.py} > $out/top.il
|
||||||
|
|
||||||
|
cat > $out/top.lpf << EOF
|
||||||
|
LOCATE COMP "clk100" SITE "P3";
|
||||||
|
IOBUF PORT "clk100" IO_TYPE=LVDS;
|
||||||
|
LOCATE COMP "serial_tx" SITE "A11";
|
||||||
|
IOBUF PORT "serial_tx" IO_TYPE=LVCMOS33;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo -n "--um-45k --package CABGA381" > $out/device
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
hx.symbiflow.buildBitstream {
|
||||||
|
name = "simplesoc-bitstream";
|
||||||
|
src = symbiflowInput;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
from nmigen import *
|
||||||
|
from nmigen.back import rtlil
|
||||||
|
|
||||||
|
from heavycomps import uart, wishbone
|
||||||
|
from minerva.core import Minerva
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleWishboneSerial(Elaboratable):
|
||||||
|
def __init__(self, tx, sys_clk_freq, baudrate=115200):
|
||||||
|
self.tx = tx
|
||||||
|
self.bus = wishbone.Interface()
|
||||||
|
self.ftw = round(2**32*baudrate/sys_clk_freq)
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
m.submodules.tx = tx = uart.RS232TX(self.ftw)
|
||||||
|
m.d.comb += [
|
||||||
|
tx.stb.eq(self.bus.cyc & self.bus.stb & self.bus.we),
|
||||||
|
tx.data.eq(self.bus.dat_w),
|
||||||
|
self.bus.ack.eq(tx.ack),
|
||||||
|
self.tx.eq(tx.tx)
|
||||||
|
]
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
class Top(Elaboratable):
|
||||||
|
def __init__(self):
|
||||||
|
self.clk100 = Signal()
|
||||||
|
self.serial_tx = Signal()
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
cd_sync = ClockDomain(reset_less=True)
|
||||||
|
m.domains += cd_sync
|
||||||
|
m.d.comb += cd_sync.clk.eq(self.clk100)
|
||||||
|
|
||||||
|
m.submodules.cpu = cpu = Minerva(with_icache=False, with_dcache=False)
|
||||||
|
m.submodules.ram = ram = wishbone.SRAM(Memory(32, 1024))
|
||||||
|
m.submodules.uart = uart = SimpleWishboneSerial(self.serial_tx, 100e6)
|
||||||
|
m.submodules.con = con = wishbone.InterconnectShared(
|
||||||
|
[cpu.ibus, cpu.dbus],
|
||||||
|
[
|
||||||
|
(lambda a: ~a[20], ram.bus),
|
||||||
|
(lambda a: a[20], uart.bus)
|
||||||
|
], register=True)
|
||||||
|
|
||||||
|
# work around https://github.com/m-labs/nmigen/issues/30
|
||||||
|
m.d.comb += [
|
||||||
|
cpu.external_interrupt.eq(0),
|
||||||
|
cpu.timer_interrupt.eq(0),
|
||||||
|
cpu.fetch.ibus.dat_w.eq(0),
|
||||||
|
cpu.fetch.ibus.sel.eq(0b1111),
|
||||||
|
cpu.fetch.ibus.we.eq(0),
|
||||||
|
cpu.fetch.ibus.cti.eq(0),
|
||||||
|
cpu.fetch.ibus.bte.eq(0),
|
||||||
|
cpu.loadstore.dbus.cti.eq(0),
|
||||||
|
cpu.loadstore.dbus.bte.eq(0),
|
||||||
|
ram.bus.err.eq(0),
|
||||||
|
uart.bus.err.eq(0),
|
||||||
|
uart.bus.dat_r.eq(0)
|
||||||
|
]
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
top = Top()
|
||||||
|
output = rtlil.convert(Fragment.get(top, None),
|
||||||
|
ports=(top.clk100, top.serial_tx))
|
||||||
|
print(output)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -1,20 +0,0 @@
|
||||||
from nmigen import *
|
|
||||||
|
|
||||||
|
|
||||||
class RoundRobin(Elaboratable):
|
|
||||||
def __init__(self, n):
|
|
||||||
self.n = n
|
|
||||||
self.request = Signal(n)
|
|
||||||
self.grant = Signal(max=n)
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
with m.Switch(self.grant):
|
|
||||||
for i in range(self.n):
|
|
||||||
with m.Case(i):
|
|
||||||
with m.If(~self.request[i]):
|
|
||||||
for j in reversed(range(i+1, i+self.n)):
|
|
||||||
t = j % self.n
|
|
||||||
with m.If(self.request[t]):
|
|
||||||
m.d.sync += self.grant.eq(t)
|
|
||||||
return m
|
|
|
@ -2,6 +2,9 @@ from nmigen import *
|
||||||
from nmigen.lib.cdc import MultiReg
|
from nmigen.lib.cdc import MultiReg
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["RS232RX", "RS232TX"]
|
||||||
|
|
||||||
|
|
||||||
class RS232RX(Elaboratable):
|
class RS232RX(Elaboratable):
|
||||||
def __init__(self, tuning_word):
|
def __init__(self, tuning_word):
|
||||||
self.rx = Signal()
|
self.rx = Signal()
|
||||||
|
@ -23,12 +26,16 @@ class RS232RX(Elaboratable):
|
||||||
rx_busy = Signal()
|
rx_busy = Signal()
|
||||||
rx_done = self.stb
|
rx_done = self.stb
|
||||||
rx_data = self.data
|
rx_data = self.data
|
||||||
m.d.sync += rx_done.eq(0)
|
m.d.sync += [
|
||||||
m.d.sync += rx_r.eq(rx)
|
rx_done.eq(0),
|
||||||
|
rx_r.eq(rx)
|
||||||
|
]
|
||||||
with m.If(~rx_busy):
|
with m.If(~rx_busy):
|
||||||
with m.If(~rx & rx_r): # look for start bit
|
with m.If(~rx & rx_r): # look for start bit
|
||||||
m.d.sync += rx_busy.eq(1)
|
m.d.sync += [
|
||||||
m.d.sync += rx_bitcount.eq(0)
|
rx_busy.eq(1),
|
||||||
|
rx_bitcount.eq(0)
|
||||||
|
]
|
||||||
with m.Else():
|
with m.Else():
|
||||||
with m.If(uart_clk_rxen):
|
with m.If(uart_clk_rxen):
|
||||||
m.d.sync += rx_bitcount.eq(rx_bitcount + 1)
|
m.d.sync += rx_bitcount.eq(rx_bitcount + 1)
|
||||||
|
@ -38,8 +45,10 @@ class RS232RX(Elaboratable):
|
||||||
with m.Elif(rx_bitcount == 9):
|
with m.Elif(rx_bitcount == 9):
|
||||||
m.d.sync += rx_busy.eq(0)
|
m.d.sync += rx_busy.eq(0)
|
||||||
with m.If(rx): # verify stop bit
|
with m.If(rx): # verify stop bit
|
||||||
m.d.sync += rx_data.eq(rx_reg)
|
m.d.sync += [
|
||||||
m.d.sync += rx_done.eq(1)
|
rx_data.eq(rx_reg),
|
||||||
|
rx_done.eq(1)
|
||||||
|
]
|
||||||
with m.Else():
|
with m.Else():
|
||||||
m.d.sync += rx_reg.eq(Cat(rx_reg[1:], rx))
|
m.d.sync += rx_reg.eq(Cat(rx_reg[1:], rx))
|
||||||
with m.If(rx_busy):
|
with m.If(rx_busy):
|
||||||
|
@ -75,23 +84,29 @@ class RS232TX(Elaboratable):
|
||||||
tx_reg = Signal(8)
|
tx_reg = Signal(8)
|
||||||
tx_bitcount = Signal(4)
|
tx_bitcount = Signal(4)
|
||||||
tx_busy = Signal()
|
tx_busy = Signal()
|
||||||
m.d.sync += self.ack.eq(0),
|
m.d.sync += self.ack.eq(0)
|
||||||
with m.If(self.stb & ~tx_busy & ~self.ack):
|
with m.If(self.stb & ~tx_busy & ~self.ack):
|
||||||
m.d.sync += tx_reg.eq(self.data)
|
m.d.sync += [
|
||||||
m.d.sync += tx_bitcount.eq(0)
|
tx_reg.eq(self.data),
|
||||||
m.d.sync += tx_busy.eq(1)
|
tx_bitcount.eq(0),
|
||||||
m.d.sync += self.tx.eq(0)
|
tx_busy.eq(1),
|
||||||
|
self.tx.eq(0)
|
||||||
|
]
|
||||||
with m.Elif(uart_clk_txen & tx_busy):
|
with m.Elif(uart_clk_txen & tx_busy):
|
||||||
m.d.sync += tx_bitcount.eq(tx_bitcount + 1)
|
m.d.sync += tx_bitcount.eq(tx_bitcount + 1)
|
||||||
with m.If(tx_bitcount == 8):
|
with m.If(tx_bitcount == 8):
|
||||||
m.d.sync += self.tx.eq(1)
|
m.d.sync += self.tx.eq(1)
|
||||||
with m.Elif(tx_bitcount == 9):
|
with m.Elif(tx_bitcount == 9):
|
||||||
m.d.sync += self.tx.eq(1)
|
m.d.sync += [
|
||||||
m.d.sync += tx_busy.eq(0)
|
self.tx.eq(1),
|
||||||
m.d.sync += self.ack.eq(1),
|
tx_busy.eq(0),
|
||||||
|
self.ack.eq(1)
|
||||||
|
]
|
||||||
with m.Else():
|
with m.Else():
|
||||||
m.d.sync += self.tx.eq(tx_reg[0])
|
m.d.sync += [
|
||||||
m.d.sync += tx_reg.eq(Cat(tx_reg[1:], 0))
|
self.tx.eq(tx_reg[0]),
|
||||||
|
tx_reg.eq(Cat(tx_reg[1:], 0))
|
||||||
|
]
|
||||||
with m.If(tx_busy):
|
with m.If(tx_busy):
|
||||||
m.d.sync += Cat(phase_accumulator_tx, uart_clk_txen).eq(phase_accumulator_tx + self.tuning_word)
|
m.d.sync += Cat(phase_accumulator_tx, uart_clk_txen).eq(phase_accumulator_tx + self.tuning_word)
|
||||||
with m.Else():
|
with m.Else():
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
from enum import Enum
|
||||||
|
from functools import reduce
|
||||||
|
from operator import or_
|
||||||
|
|
||||||
|
from nmigen import *
|
||||||
|
from nmigen.hdl.rec import *
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["Cycle", "Interface", "Arbiter", "Decoder", "InterconnectShared"]
|
||||||
|
|
||||||
|
|
||||||
|
class Cycle(Enum):
|
||||||
|
CLASSIC = 0
|
||||||
|
CONSTANT = 1
|
||||||
|
INCREMENT = 2
|
||||||
|
END = 7
|
||||||
|
|
||||||
|
|
||||||
|
wishbone_layout = [
|
||||||
|
("adr", 30, DIR_FANOUT),
|
||||||
|
("dat_w", 32, DIR_FANOUT),
|
||||||
|
("dat_r", 32, DIR_FANIN),
|
||||||
|
("sel", 4, DIR_FANOUT),
|
||||||
|
("cyc", 1, DIR_FANOUT),
|
||||||
|
("stb", 1, DIR_FANOUT),
|
||||||
|
("ack", 1, DIR_FANIN),
|
||||||
|
("we", 1, DIR_FANOUT),
|
||||||
|
("cti", 3, DIR_FANOUT),
|
||||||
|
("bte", 2, DIR_FANOUT),
|
||||||
|
("err", 1, DIR_FANIN)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Interface(Record):
|
||||||
|
def __init__(self):
|
||||||
|
Record.__init__(self, wishbone_layout)
|
||||||
|
|
||||||
|
def _do_transaction(self):
|
||||||
|
yield self.cyc.eq(1)
|
||||||
|
yield self.stb.eq(1)
|
||||||
|
yield
|
||||||
|
while not (yield self.ack):
|
||||||
|
yield
|
||||||
|
yield self.cyc.eq(0)
|
||||||
|
yield self.stb.eq(0)
|
||||||
|
|
||||||
|
def write(self, adr, dat, sel=None):
|
||||||
|
if sel is None:
|
||||||
|
sel = 2**len(self.sel) - 1
|
||||||
|
yield self.adr.eq(adr)
|
||||||
|
yield self.dat_w.eq(dat)
|
||||||
|
yield self.sel.eq(sel)
|
||||||
|
yield self.we.eq(1)
|
||||||
|
yield from self._do_transaction()
|
||||||
|
|
||||||
|
def read(self, adr):
|
||||||
|
yield self.adr.eq(adr)
|
||||||
|
yield self.we.eq(0)
|
||||||
|
yield from self._do_transaction()
|
||||||
|
return (yield self.dat_r)
|
||||||
|
|
||||||
|
|
||||||
|
class SRAM(Elaboratable):
|
||||||
|
def __init__(self, mem, read_only=False, bus=None):
|
||||||
|
self.mem = mem
|
||||||
|
self.read_only = read_only
|
||||||
|
if bus is None:
|
||||||
|
bus = Interface()
|
||||||
|
self.bus = bus
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
if self.mem.width > len(self.bus.dat_r):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# read
|
||||||
|
m.submodules.rdport = rdport = self.mem.read_port()
|
||||||
|
m.d.comb += [
|
||||||
|
rdport.addr.eq(self.bus.adr[:len(rdport.addr)]),
|
||||||
|
self.bus.dat_r.eq(rdport.data)
|
||||||
|
]
|
||||||
|
|
||||||
|
# write
|
||||||
|
if not self.read_only:
|
||||||
|
m.submodules.wrport = wrport = self.mem.write_port(granularity=8)
|
||||||
|
m.d.comb += [
|
||||||
|
wrport.addr.eq(self.bus.adr[:len(rdport.addr)]),
|
||||||
|
wrport.data.eq(self.bus.dat_w)
|
||||||
|
]
|
||||||
|
for i in range(4):
|
||||||
|
m.d.comb += wrport.en[i].eq(self.bus.cyc & self.bus.stb & self.bus.we & self.bus.sel[i])
|
||||||
|
|
||||||
|
# generate ack
|
||||||
|
m.d.sync += self.bus.ack.eq(0)
|
||||||
|
with m.If(self.bus.cyc & self.bus.stb & ~self.bus.ack):
|
||||||
|
m.d.sync += self.bus.ack.eq(1)
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
class RoundRobin(Elaboratable):
|
||||||
|
def __init__(self, n):
|
||||||
|
self.n = n
|
||||||
|
self.request = Signal(n)
|
||||||
|
self.grant = Signal(max=n)
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
with m.Switch(self.grant):
|
||||||
|
for i in range(self.n):
|
||||||
|
with m.Case(i):
|
||||||
|
with m.If(~self.request[i]):
|
||||||
|
for j in reversed(range(i+1, i+self.n)):
|
||||||
|
t = j % self.n
|
||||||
|
with m.If(self.request[t]):
|
||||||
|
m.d.sync += self.grant.eq(t)
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
class Arbiter(Elaboratable):
|
||||||
|
def __init__(self, masters, target):
|
||||||
|
self.masters = masters
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.submodules.rr = rr = RoundRobin(len(self.masters))
|
||||||
|
|
||||||
|
# mux master->target signals
|
||||||
|
for name, size, direction in wishbone_layout:
|
||||||
|
if direction == DIR_FANOUT:
|
||||||
|
choices = Array(getattr(m, name) for m in self.masters)
|
||||||
|
m.d.comb += getattr(self.target, name).eq(choices[rr.grant])
|
||||||
|
|
||||||
|
# connect target->master signals
|
||||||
|
for name, size, direction in wishbone_layout:
|
||||||
|
if direction == DIR_FANIN:
|
||||||
|
source = getattr(self.target, name)
|
||||||
|
for i, master in enumerate(self.masters):
|
||||||
|
dest = getattr(master, name)
|
||||||
|
if name == "ack" or name == "err":
|
||||||
|
m.d.comb += dest.eq(source & (rr.grant == i))
|
||||||
|
else:
|
||||||
|
m.d.comb += dest.eq(source)
|
||||||
|
|
||||||
|
# connect bus requests to round-robin selector
|
||||||
|
reqs = [m.cyc & ~m.ack for m in self.masters]
|
||||||
|
m.d.comb += rr.request.eq(Cat(*reqs))
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
class Decoder(Elaboratable):
|
||||||
|
def __init__(self, master, targets, register=False):
|
||||||
|
self.master = master
|
||||||
|
self.targets = targets
|
||||||
|
self.register = register
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
nt = len(self.targets)
|
||||||
|
target_sel = Signal(nt)
|
||||||
|
target_sel_r = Signal(nt)
|
||||||
|
|
||||||
|
# decode target addresses
|
||||||
|
for i, (fun, bus) in enumerate(self.targets):
|
||||||
|
m.d.comb += target_sel[i].eq(fun(self.master.adr))
|
||||||
|
if self.register:
|
||||||
|
m.d.sync += target_sel_r.eq(target_sel)
|
||||||
|
else:
|
||||||
|
m.d.comb += target_sel_r.eq(target_sel)
|
||||||
|
|
||||||
|
# connect master->targets signals except cyc
|
||||||
|
for target in self.targets:
|
||||||
|
for name, size, direction in wishbone_layout:
|
||||||
|
if direction == DIR_FANOUT and name != "cyc":
|
||||||
|
m.d.comb += getattr(target[1], name).eq(getattr(self.master, name))
|
||||||
|
|
||||||
|
# combine cyc with target selection signals
|
||||||
|
for i, target in enumerate(self.targets):
|
||||||
|
m.d.comb += target[1].cyc.eq(self.master.cyc & target_sel[i])
|
||||||
|
|
||||||
|
# generate master ack (resp. err) by ORing all target acks (resp. errs)
|
||||||
|
m.d.comb += [
|
||||||
|
self.master.ack.eq(reduce(or_, [target[1].ack for target in self.targets])),
|
||||||
|
self.master.err.eq(reduce(or_, [target[1].err for target in self.targets]))
|
||||||
|
]
|
||||||
|
|
||||||
|
# mux (1-hot) target data return
|
||||||
|
masked = [Repl(target_sel_r[i], len(self.master.dat_r)) & self.targets[i][1].dat_r for i in range(nt)]
|
||||||
|
m.d.comb += self.master.dat_r.eq(reduce(or_, masked))
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
class InterconnectShared(Module):
|
||||||
|
def __init__(self, masters, targets, register=False):
|
||||||
|
self.masters = masters
|
||||||
|
self.targets = targets
|
||||||
|
self.register = register
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
shared = Interface()
|
||||||
|
m.submodules.arbiter = Arbiter(self.masters, shared)
|
||||||
|
m.submodules.decoder = Decoder(shared, self.targets, self.register)
|
||||||
|
return m
|
|
@ -4,6 +4,7 @@ let
|
||||||
jobs = derivations // {
|
jobs = derivations // {
|
||||||
helloworld_ecp5 = import ./examples/helloworld_ecp5.nix { inherit pkgs; };
|
helloworld_ecp5 = import ./examples/helloworld_ecp5.nix { inherit pkgs; };
|
||||||
helloworld_kintex7 = import ./examples/helloworld_kintex7.nix { inherit pkgs; };
|
helloworld_kintex7 = import ./examples/helloworld_kintex7.nix { inherit pkgs; };
|
||||||
|
simplesoc_ecp5 = import ./examples/simplesoc_ecp5.nix { inherit pkgs; };
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
builtins.mapAttrs (name: value: pkgs.lib.hydraJob value) jobs
|
builtins.mapAttrs (name: value: pkgs.lib.hydraJob value) jobs
|
||||||
|
|
Loading…
Reference in New Issue