FPGA flake

main
xenia 2024-03-16 23:41:51 +01:00
parent d766c8434a
commit 4642bafae1
9 changed files with 296 additions and 0 deletions

View File

@ -27,5 +27,10 @@
description = "Minimal python flake";
welcomeText = "hisss 🐍🐍🐍";
};
templates.verilog= {
path = ./fpga;
description = "FPGA flake, running verilog on ice40";
welcomeText = "mjau 🧊";
};
};
}

1
fpga/.envrc 100644
View File

@ -0,0 +1 @@
use flake

3
fpga/.gitignore vendored 100644
View File

@ -0,0 +1,3 @@
abc.history
.direnv
result

59
fpga/flake.lock 100644
View File

@ -0,0 +1,59 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1697009197,
"narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

80
fpga/flake.nix 100644
View File

@ -0,0 +1,80 @@
{
description = "FPGA flake, running verilog on ice40";
inputs = {
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (sys:
let pkgs = nixpkgs.legacyPackages.${sys};
verilator = import ./verilator.nix pkgs ;
yosys = pkgs.yosys;
vflags = ''-DSIMULATE -Wno-fatal -Wpedantic -Wwarn-lint -Wwarn-style -Wno-PINCONNECTEMPTY -Wno-BLKSEQ -CFLAGS "-Wpedantic -std=c++20"'';
verilate-src = cmd: ''
cp -r ${./src} ./src
cp -r ${./simulation} ./simulation
find ./src/ -name '*.v' -exec ${verilator}/bin/verilator ${vflags} ${cmd} {} +
'';
lint = pkgs.runCommand "lint" {} ''
${verilate-src "--lint-only"}
echo "compiler didn't get angry :3"
: 3 > $out
'';
test-trace = pkgs.runCommandCC "test-trace" {} ''
set -e
${verilate-src "--cc --build --exe --trace -CFLAGS -DTRACE=1 ./simulation/test_led.cpp -top top"}
mv obj_dir "$out" && mkdir "$out/bin"
cp "$out/Vtop" "$out/bin/sim"
$out/bin/sim $out/trace.vcd
echo "${pkgs.gtkwave}/bin/gtkwave $out/trace.vcd" > $out/bin/test-trace
chmod u+x $out/bin/test-trace
'';
synth = pkgs.runCommandCC "synth" {} ''
mkdir -p "$out"
find ${./src} -name '*.v' -exec ${yosys}/bin/yosys -f ' -sv' -q -p "synth_ice40 -top top -json $out/synth.json" {} +
'';
pnr-interactive = pkgs.writeScriptBin "pnr-interactive" ''
${pkgs.nextpnrWithGui}/bin/nextpnr-ice40 --up5k --package sg48 --pcf ${./pins.pcf} --json ${synth}/synth.json --gui
'';
pnr = pkgs.runCommandCC "pnr" {} ''
mkdir -p "$out"
${pkgs.nextpnrWithGui}/bin/nextpnr-ice40 --up5k --package sg48 --pcf ${./pins.pcf} --json ${synth}/synth.json --asc "$out/pnr.asc" \
--freq 50
'';
flash = pkgs.writeScriptBin "flash" ''
set -e
bin="$(mktemp)"
${pkgs.icestorm}/bin/icepack ${pnr}/pnr.asc "$bin"
${pkgs.icestorm}/bin/iceprog "$bin"
'';
deps = [
yosys pkgs.nextpnrWithGui pkgs.icestorm verilator pkgs.gtkwave
pkgs.picocom
];
in rec {
packages.verilator = verilator;
packages.lint = lint;
packages.test-trace = test-trace;
packages.synth = synth;
packages.pnr-interactive = pnr-interactive;
packages.pnr = pnr;
packages.flash = flash;
devShells.default = pkgs.mkShell { packages = deps; };
}
);
}

2
fpga/pins.pcf 100644
View File

@ -0,0 +1,2 @@
set_io clk_12m 35
set_io led_red 26

View File

@ -0,0 +1,56 @@
#include "Vtop.h"
#include "verilated.h"
#include <stdint.h>
#include <iostream>
#include <random>
#include "verilated_vcd_c.h"
struct state {
VerilatedContext *ctx;
Vtop *vtop;
VerilatedVcdC *trace;
};
void posedge(state &state) {
state.ctx->timeInc(1);
state.vtop->clk_12m = 1;
state.vtop->eval();
state.trace->dump(state.ctx->time());
state.ctx->timeInc(1);
state.vtop->clk_12m = 0;
state.vtop->eval();
state.trace->dump(state.ctx->time());
}
int main(int argc, char **argv) {
VerilatedContext *vctx = new VerilatedContext;
Verilated::traceEverOn(true);
Vtop *vtop = new Vtop(vctx);
if (argc != 2) {
std::cout << "Run with argument for destination!" << std::endl;
return 1;
}
VerilatedVcdC *trace = new VerilatedVcdC;
vtop->trace(trace, 99);
trace->open(argv[1]);
std::cout << "(writing trace to " << argv[1] << ")" << std::endl;
state state = {
.ctx = vctx,
.vtop = vtop,
.trace = trace,
};
for (int i = 0; i < 2<<24; i++) {
posedge(state);
}
state.trace->close();
}

43
fpga/src/top.v 100644
View File

@ -0,0 +1,43 @@
module top(
input clk_12m,
output led_red
);
// want clock to blink at a rate of 1Hz
// intermediate clock at 2^24 Hz = 16.77MHz
// 12MHz * 7/5 = 16.8MHz, within 0.2%
wire clk_16_8m;
wire clk_stable;
// set ethernet clock to 50 MHz
`ifndef SIMULATE
SB_PLL40_PAD #(
.FEEDBACK_PATH("SIMPLE"),
.DIVR(4'd4), // divide by 4+1=5
.DIVF(7'd6), // multiply by 6+1=7
.DIVQ(3'd0), // divide by 2^0
.FILTER_RANGE(3'b001)
) SB_PLL40_PAD_inst (
.PACKAGEPIN(clk_12m),
.PLLOUTGLOBAL(clk_16_8m),
.RESETB(1'b1),
.BYPASS(1'b0),
.LOCK(clk_stable)
);
`else
// in simulation, tie clocks together
assign clk_16_8m = clk_12m;
assign clk_stable = 1;
`endif
// divide 16.8MHz clock by 2^24
reg [24:0] clk_ctr;
always @(posedge clk_16_8m) begin
clk_ctr <= clk_ctr + 1;
end
assign led_red = clk_stable & clk_ctr[24];
endmodule

47
fpga/verilator.nix 100644
View File

@ -0,0 +1,47 @@
pkgs: with pkgs;
# From https://github.com/NixOS/nixpkgs/blob/nixos-23.11/pkgs/applications/science/electronics/verilator/default.nix
# Patches out SystemC-support, as the SystemC does not build on Darwin
stdenv.mkDerivation rec {
pname = "verilator";
version = "5.018";
src = fetchFromGitHub {
owner = pname;
repo = pname;
rev = "v${version}";
hash = "sha256-f06UzNw2MQ5me03EPrVFhkwxKum/GLDzQbDNTBsJMJs=";
};
enableParallelBuilding = true;
buildInputs = [ perl python3 ]; # ccache
nativeBuildInputs = [ makeWrapper flex bison autoconf help2man git ];
nativeCheckInputs = [ which numactl ]; # cmake
doCheck = stdenv.isLinux; # darwin tests are broken for now...
checkTarget = "test";
preConfigure = "autoconf";
postPatch = ''
patchShebangs bin/* src/* nodist/* docs/bin/* examples/xml_py/* \
test_regress/{driver.pl,t/*.{pl,pf}} \
ci/* ci/docker/run/* ci/docker/run/hooks/* ci/docker/buildenv/build.sh
'';
# grep '^#!/' -R . | grep -v /nix/store | less
# (in nix-shell after patchPhase)
postInstall = lib.optionalString stdenv.isLinux ''
for x in $(ls $out/bin/verilator*); do
wrapProgram "$x" --set LOCALE_ARCHIVE "${glibcLocales}/lib/locale/locale-archive"
done
'';
meta = with lib; {
description = "Fast and robust (System)Verilog simulator/compiler and linter";
homepage = "https://www.veripool.org/verilator";
license = with licenses; [ lgpl3Only artistic2 ];
platforms = platforms.unix;
maintainers = with maintainers; [ thoughtpolice amiloradovsky ];
};
}