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
|
||||
|
||||
|
||||
__all__ = ["RS232RX", "RS232TX"]
|
||||
|
||||
|
||||
class RS232RX(Elaboratable):
|
||||
def __init__(self, tuning_word):
|
||||
self.rx = Signal()
|
||||
|
@ -23,12 +26,16 @@ class RS232RX(Elaboratable):
|
|||
rx_busy = Signal()
|
||||
rx_done = self.stb
|
||||
rx_data = self.data
|
||||
m.d.sync += rx_done.eq(0)
|
||||
m.d.sync += rx_r.eq(rx)
|
||||
m.d.sync += [
|
||||
rx_done.eq(0),
|
||||
rx_r.eq(rx)
|
||||
]
|
||||
with m.If(~rx_busy):
|
||||
with m.If(~rx & rx_r): # look for start bit
|
||||
m.d.sync += rx_busy.eq(1)
|
||||
m.d.sync += rx_bitcount.eq(0)
|
||||
m.d.sync += [
|
||||
rx_busy.eq(1),
|
||||
rx_bitcount.eq(0)
|
||||
]
|
||||
with m.Else():
|
||||
with m.If(uart_clk_rxen):
|
||||
m.d.sync += rx_bitcount.eq(rx_bitcount + 1)
|
||||
|
@ -38,8 +45,10 @@ class RS232RX(Elaboratable):
|
|||
with m.Elif(rx_bitcount == 9):
|
||||
m.d.sync += rx_busy.eq(0)
|
||||
with m.If(rx): # verify stop bit
|
||||
m.d.sync += rx_data.eq(rx_reg)
|
||||
m.d.sync += rx_done.eq(1)
|
||||
m.d.sync += [
|
||||
rx_data.eq(rx_reg),
|
||||
rx_done.eq(1)
|
||||
]
|
||||
with m.Else():
|
||||
m.d.sync += rx_reg.eq(Cat(rx_reg[1:], rx))
|
||||
with m.If(rx_busy):
|
||||
|
@ -75,23 +84,29 @@ class RS232TX(Elaboratable):
|
|||
tx_reg = Signal(8)
|
||||
tx_bitcount = Signal(4)
|
||||
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):
|
||||
m.d.sync += tx_reg.eq(self.data)
|
||||
m.d.sync += tx_bitcount.eq(0)
|
||||
m.d.sync += tx_busy.eq(1)
|
||||
m.d.sync += self.tx.eq(0)
|
||||
m.d.sync += [
|
||||
tx_reg.eq(self.data),
|
||||
tx_bitcount.eq(0),
|
||||
tx_busy.eq(1),
|
||||
self.tx.eq(0)
|
||||
]
|
||||
with m.Elif(uart_clk_txen & tx_busy):
|
||||
m.d.sync += tx_bitcount.eq(tx_bitcount + 1)
|
||||
with m.If(tx_bitcount == 8):
|
||||
m.d.sync += self.tx.eq(1)
|
||||
with m.Elif(tx_bitcount == 9):
|
||||
m.d.sync += self.tx.eq(1)
|
||||
m.d.sync += tx_busy.eq(0)
|
||||
m.d.sync += self.ack.eq(1),
|
||||
m.d.sync += [
|
||||
self.tx.eq(1),
|
||||
tx_busy.eq(0),
|
||||
self.ack.eq(1)
|
||||
]
|
||||
with m.Else():
|
||||
m.d.sync += self.tx.eq(tx_reg[0])
|
||||
m.d.sync += tx_reg.eq(Cat(tx_reg[1:], 0))
|
||||
m.d.sync += [
|
||||
self.tx.eq(tx_reg[0]),
|
||||
tx_reg.eq(Cat(tx_reg[1:], 0))
|
||||
]
|
||||
with m.If(tx_busy):
|
||||
m.d.sync += Cat(phase_accumulator_tx, uart_clk_txen).eq(phase_accumulator_tx + self.tuning_word)
|
||||
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 // {
|
||||
helloworld_ecp5 = import ./examples/helloworld_ecp5.nix { inherit pkgs; };
|
||||
helloworld_kintex7 = import ./examples/helloworld_kintex7.nix { inherit pkgs; };
|
||||
simplesoc_ecp5 = import ./examples/simplesoc_ecp5.nix { inherit pkgs; };
|
||||
};
|
||||
in
|
||||
builtins.mapAttrs (name: value: pkgs.lib.hydraJob value) jobs
|
||||
|
|
Loading…
Reference in New Issue