diff --git a/default.nix b/default.nix index acca276..bc8405c 100644 --- a/default.nix +++ b/default.nix @@ -1,4 +1,8 @@ { pkgs }: -(import ./derivations.nix { inherit pkgs; }) // { - vivado = import ./eda/vivado.nix { inherit pkgs; }; -} +let + hx = import ./derivations.nix { inherit pkgs; }; +in + hx // { + symbiflow = import ./eda/symbiflow.nix { inherit pkgs; yosys = hx.yosys; }; + vivado = import ./eda/vivado.nix { inherit pkgs; }; + } diff --git a/eda/symbiflow.nix b/eda/symbiflow.nix new file mode 100644 index 0000000..1dae74c --- /dev/null +++ b/eda/symbiflow.nix @@ -0,0 +1,15 @@ +{ pkgs, yosys }: +{ + buildBitstream = { name, src }: + pkgs.stdenv.mkDerivation { + inherit name src; + phases = [ "buildPhase" ]; + buildPhase = + '' + mkdir $out + ${yosys}/bin/yosys -p "read_ilang $src/top.il; synth_ecp5 -top top -json $out/top.json" + ${pkgs.nextpnr}/bin/nextpnr-ecp5 --json $out/top.json --textcfg $out/top.config `cat $src/device` --lpf $src/top.lpf + ${pkgs.trellis}/bin/ecppack --svf-rowsize 100000 --svf $out/top.svf $out/top.config $out/top.bit + ''; + }; +} diff --git a/examples/helloworld_ecp5.nix b/examples/helloworld_ecp5.nix new file mode 100644 index 0000000..6e13c66 --- /dev/null +++ b/examples/helloworld_ecp5.nix @@ -0,0 +1,26 @@ +{ pkgs ? import {} +, hx ? import ../default.nix { inherit pkgs; }}: + +let + symbiflowInput = pkgs.runCommand "helloworld-symbiflow-input" { + buildInputs = [ (pkgs.python3.withPackages(ps: [hx.nmigen hx.heavycomps])) hx.yosys ]; + } + '' + mkdir $out + + python ${./helloworld_ecp5.py} > $out/top.il + + cat > $out/top.lpf << EOF + LOCATE COMP "serial_tx" SITE "A4"; + IOBUF PORT "P3" IO_TYPE=LVDS; + LOCATE COMP "tx" SITE "B19"; + IOBUF PORT "C11" IO_TYPE=LVCMOS33; + EOF + + echo -n "--um-45k --package CABGA381" > $out/device + ''; +in + hx.symbiflow.buildBitstream { + name = "helloworld-bitstream"; + src = symbiflowInput; + } diff --git a/examples/helloworld_ecp5.py b/examples/helloworld_ecp5.py new file mode 100644 index 0000000..a209094 --- /dev/null +++ b/examples/helloworld_ecp5.py @@ -0,0 +1,49 @@ +from nmigen import * +from nmigen.back import rtlil + +from heavycomps import uart + + +class Top(Elaboratable): + def __init__(self, baudrate=115200): + self.baudrate = baudrate + 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) + + string = "Hello World!\r\n" + mem = Memory(width=8, depth=len(string), + init=[ord(c) for c in string]) + m.submodules.rdport = rdport = mem.read_port(synchronous=False) + + tx = uart.RS232TX(round(2**32*self.baudrate/100e6)) + m.submodules.tx = tx + m.d.comb += [ + tx.stb.eq(1), + tx.data.eq(rdport.data), + self.serial_tx.eq(tx.tx) + ] + + with m.If(tx.ack): + with m.If(rdport.addr == len(string) - 1): + m.d.sync += rdport.addr.eq(0) + with m.Else(): + m.d.sync += rdport.addr.eq(rdport.addr + 1) + + 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()