use nmigen build system, upstream yosys, reorganize

This commit is contained in:
Sebastien Bourdeauducq 2019-07-03 18:51:45 +08:00
parent ca293ee7cc
commit af51f6fe8f
12 changed files with 106 additions and 681 deletions

View File

@ -1,9 +1,7 @@
{ pkgs }: { pkgs }:
rec { rec {
drvs = rec { vivado = import ./eda/vivado.nix { inherit pkgs; };
yosys = pkgs.callPackage ./eda/yosys.nix {}; nmigen = pkgs.callPackage ./eda/nmigen.nix { };
symbiyosys = pkgs.symbiyosys.override { inherit yosys; };
nmigen = pkgs.callPackage ./eda/nmigen.nix { inherit yosys; };
nmigen-boards = pkgs.callPackage ./eda/nmigen-boards.nix { inherit nmigen; }; nmigen-boards = pkgs.callPackage ./eda/nmigen-boards.nix { inherit nmigen; };
scala-spinalhdl = pkgs.callPackage ./eda/scala-spinalhdl.nix {}; scala-spinalhdl = pkgs.callPackage ./eda/scala-spinalhdl.nix {};
@ -26,9 +24,4 @@ rec {
rust-riscv32i-crates = pkgs.callPackage ./compilers/rust-riscv32i-crates.nix { }; rust-riscv32i-crates = pkgs.callPackage ./compilers/rust-riscv32i-crates.nix { };
fw-helloworld = pkgs.callPackage ./firmware { inherit rust-riscv32i-crates binutils-riscv32; }; fw-helloworld = pkgs.callPackage ./firmware { inherit rust-riscv32i-crates binutils-riscv32; };
};
lib = {
symbiflow = import ./eda/symbiflow.nix { inherit pkgs; inherit (drvs) yosys; };
vivado = import ./eda/vivado.nix { inherit pkgs; };
};
} }

View File

@ -1,15 +0,0 @@
{ 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 -nomux -json $out/top.json"
${pkgs.nextpnr}/bin/nextpnr-ecp5 --json $out/top.json --textcfg $out/top.config `cat $src/device` --lpf $src/top.lpf --freq 100
${pkgs.trellis}/bin/ecppack --svf-rowsize 100000 --svf $out/top.svf $out/top.config $out/top.bit
'';
};
}

View File

@ -1,10 +1,10 @@
# Install Vivado in /opt and add to /etc/nixos/configuration.nix: # Install Vivado in /opt and add to /etc/nixos/configuration.nix:
# nix.sandboxPaths = ["/opt"]; # nix.sandboxPaths = ["/opt"];
{ pkgs }: { pkgs, vivadoPath ? "/opt/Xilinx/Vivado/2018.3" }:
let
vivadoEnv = pkgs.buildFHSUserEnv { pkgs.buildFHSUserEnv {
name = "vivado-env"; name = "vivado";
targetPkgs = pkgs: ( targetPkgs = pkgs: (
with pkgs; [ with pkgs; [
ncurses5 ncurses5
@ -19,19 +19,6 @@ let
xorg.libXi xorg.libXi
] ]
); );
}; profile = "source ${vivadoPath}/settings64.sh";
in runScript = "vivado";
{
buildBitstream = { name, src, vivadoPath ? "/opt/Xilinx/Vivado/2018.3" }:
pkgs.stdenv.mkDerivation {
inherit name src;
phases = [ "buildPhase" ];
buildPhase =
''
cp --no-preserve=mode,ownership -R $src/* .
${vivadoEnv}/bin/vivado-env -c "source ${vivadoPath}/settings64.sh && vivado -mode batch -source top.tcl"
mkdir $out
cp *.dcp *.rpt *.bit $out
'';
};
} }

View File

@ -1,78 +0,0 @@
{ stdenv, fetchFromGitHub
, pkgconfig, bison, flex
, tcl, readline, libffi, python3
, protobuf
}:
with builtins;
stdenv.mkDerivation rec {
name = "yosys-${version}";
version = "2019.04.27hx";
srcs = [
(fetchFromGitHub {
owner = "yosyshq";
repo = "yosys";
rev = "ea0e0722bb42254ac8c63eb41664d9dfb7973aec";
sha256 = "1sq9a1h651is9wq8sq4kb4n8v4d91fmdc7g01nrxj1vk1nji8308";
name = "yosys";
})
# NOTE: the version of abc used here is synchronized with
# the one in the yosys Makefile of the version above;
# keep them the same for quality purposes.
(fetchFromGitHub {
owner = "berkeley-abc";
repo = "abc";
rev = "3709744c60696c5e3f4cc123939921ce8107fe04";
sha256 = "18a9cjng3qfalq8m9az5ck1y5h4l2pf9ycrvkzs9hn82b1j7vrax";
name = "yosys-abc";
})
];
sourceRoot = "yosys";
enableParallelBuilding = true;
nativeBuildInputs = [ pkgconfig ];
buildInputs = [ tcl readline libffi python3 bison flex protobuf ];
makeFlags = [ "ENABLE_PROTOBUF=1" ];
patchPhase = ''
patch -p1 < ${./yosys_726.patch}
substituteInPlace ../yosys-abc/Makefile \
--replace 'CC := gcc' ""
substituteInPlace ./Makefile \
--replace 'CXX = clang' "" \
--replace 'ABCMKARGS = CC="$(CXX)"' 'ABCMKARGS =' \
--replace 'echo UNKNOWN' 'echo ${substring 0 10 (elemAt srcs 0).rev}'
'';
preBuild = ''
chmod -R u+w ../yosys-abc
ln -s ../yosys-abc abc
make config-${if stdenv.cc.isClang or false then "clang" else "gcc"}
echo 'ABCREV := default' >> Makefile.conf
makeFlags="PREFIX=$out $makeFlags"
# we have to do this ourselves for some reason...
(cd misc && ${protobuf}/bin/protoc --cpp_out ../backends/protobuf/ ./yosys.proto)
'';
meta = {
description = "Framework for RTL synthesis tools";
longDescription = ''
Yosys is a framework for RTL synthesis tools. It currently has
extensive Verilog-2005 support and provides a basic set of
synthesis algorithms for various application domains.
Yosys can be adapted to perform any synthesis job by combining
the existing passes (algorithms) using synthesis scripts and
adding additional passes as needed by extending the yosys C++
code base.
'';
homepage = http://www.clifford.at/yosys/;
license = stdenv.lib.licenses.isc;
maintainers = with stdenv.lib.maintainers; [ shell thoughtpolice ];
platforms = stdenv.lib.platforms.unix;
};
}

View File

@ -1,399 +0,0 @@
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index 83d83f48..ef6f102c 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -25,6 +25,7 @@
#include "kernel/celltypes.h"
#include "kernel/log.h"
#include "kernel/sigtools.h"
+#include "kernel/modtools.h"
#include <string>
#include <sstream>
#include <set>
@@ -33,15 +34,17 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal, siminit;
+bool verbose, norename, noattr, attr2comment, noexpr, nodec, noinline, nohex, nostr, defparam, decimal, siminit;
int auto_name_counter, auto_name_offset, auto_name_digits;
std::map<RTLIL::IdString, int> auto_name_map;
std::set<RTLIL::IdString> reg_wires, reg_ct;
+std::map<RTLIL::Wire*, int> proc_consumed_wires;
std::string auto_prefix;
RTLIL::Module *active_module;
dict<RTLIL::SigBit, RTLIL::State> active_initdata;
SigMap active_sigmap;
+ModIndex active_modindex;
void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
{
@@ -183,6 +186,72 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string &reg_name)
return true;
}
+bool can_inline_cell_expr(RTLIL::Cell *cell)
+{
+ static pool<IdString> inlinable_cells = {
+ "$not", "$pos", "$neg",
+ "$and", "$or", "$xor", "$xnor",
+ "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
+ "$shl", "$shr", "$sshl", "$sshr",
+ "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
+ "$add", "$sub", "$mul", "$div", "$mod", "$pow",
+ "$logic_not", "$logic_and", "$logic_or",
+ "$mux"
+ };
+
+ if (noinline)
+ return false;
+
+ if (!inlinable_cells.count(cell->type))
+ return false;
+
+ const RTLIL::SigSpec &output = cell->getPort("\\Y");
+ if (!output.is_wire())
+ return false;
+
+ RTLIL::Wire *output_wire = output.as_wire();
+ if (output_wire->port_id || (!noattr && output_wire->attributes.size()))
+ return false;
+
+ pool<ModIndex::PortInfo> ports = active_modindex.query_ports(output[0]);
+ if (proc_consumed_wires[output_wire] == 1 && ports.size() == 1)
+ return true;
+
+ if (proc_consumed_wires[output_wire] == 0 && ports.size() == 2)
+ {
+ auto port1 = ports.pop();
+ auto port2 = ports.pop();
+ return port1.cell->getPort(port1.port).size() ==
+ port2.cell->getPort(port2.port).size();
+ }
+
+ return false;
+}
+
+bool can_inline_wire(RTLIL::Wire *wire)
+{
+ if (noinline)
+ return false;
+
+ RTLIL::SigSpec wire_spec = RTLIL::SigSpec(wire);
+ if (wire_spec.empty())
+ return false;
+
+ pool<ModIndex::PortInfo> ports = active_modindex.query_ports(wire_spec[0]);
+ if (ports.size() > 2)
+ return false;
+
+ for (auto &port_info : ports)
+ {
+ if (port_info.cell->name[0] == '$' && port_info.port == "\\Y")
+ {
+ if (can_inline_cell_expr(port_info.cell))
+ return true;
+ }
+ }
+ return false;
+}
+
void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false)
{
if (width < 0)
@@ -313,13 +382,29 @@ void dump_reg_init(std::ostream &f, SigSpec sig)
}
}
+bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell, bool do_inline);
+
void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false)
{
if (chunk.wire == NULL) {
dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal);
} else {
if (chunk.width == chunk.wire->width && chunk.offset == 0) {
- f << stringf("%s", id(chunk.wire->name).c_str());
+ if (can_inline_wire(chunk.wire))
+ {
+ f << "(";
+ pool<ModIndex::PortInfo> ports = active_modindex.query_ports(SigSpec(chunk)[0]);
+ for (auto &port_info : ports)
+ {
+ if (port_info.port == "\\Y")
+ dump_cell_expr(f, "", port_info.cell, true);
+ }
+ f << ")";
+ }
+ else
+ {
+ f << stringf("%s", id(chunk.wire->name).c_str());
+ }
} else if (chunk.width == 1) {
if (chunk.wire->upto)
f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
@@ -372,6 +457,9 @@ void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString,
void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
{
+ if (can_inline_wire(wire))
+ return;
+
dump_attributes(f, indent, wire->attributes);
#if 0
if (wire->port_input && !wire->port_output)
@@ -464,30 +552,54 @@ no_special_reg_name:
}
}
-void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
+void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op, bool do_inline)
{
- f << stringf("%s" "assign ", indent.c_str());
- dump_sigspec(f, cell->getPort("\\Y"));
- f << stringf(" = %s ", op.c_str());
+ if (!do_inline)
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ }
+ f << stringf("%s ", op.c_str());
dump_attributes(f, "", cell->attributes, ' ');
dump_cell_expr_port(f, cell, "A", true);
- f << stringf(";\n");
+ if (!do_inline)
+ {
+ f << stringf(";\n");
+ }
}
-void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
+void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op, bool do_inline)
{
- f << stringf("%s" "assign ", indent.c_str());
- dump_sigspec(f, cell->getPort("\\Y"));
- f << stringf(" = ");
+ if (!do_inline)
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ }
+ else
+ {
+ f << stringf("(");
+ }
dump_cell_expr_port(f, cell, "A", true);
f << stringf(" %s ", op.c_str());
dump_attributes(f, "", cell->attributes, ' ');
dump_cell_expr_port(f, cell, "B", true);
- f << stringf(";\n");
+ if (!do_inline)
+ {
+ f << stringf(";\n");
+ }
+ else
+ {
+ f << stringf(")");
+ }
}
-bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
+bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell, bool do_inline)
{
+ if (can_inline_cell_expr(cell) && !do_inline)
+ return true;
+
if (cell->type == "$_NOT_") {
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, cell->getPort("\\Y"));
@@ -658,9 +770,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
}
#define HANDLE_UNIOP(_type, _operator) \
- if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
+ if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator, do_inline); return true; }
#define HANDLE_BINOP(_type, _operator) \
- if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; }
+ if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator, do_inline); return true; }
HANDLE_UNIOP("$not", "~")
HANDLE_UNIOP("$pos", "+")
@@ -756,16 +868,30 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
if (cell->type == "$mux")
{
- f << stringf("%s" "assign ", indent.c_str());
- dump_sigspec(f, cell->getPort("\\Y"));
- f << stringf(" = ");
+ if (!do_inline)
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ }
+ else
+ {
+ f << stringf("(");
+ }
dump_sigspec(f, cell->getPort("\\S"));
f << stringf(" ? ");
dump_attributes(f, "", cell->attributes, ' ');
dump_sigspec(f, cell->getPort("\\B"));
f << stringf(" : ");
dump_sigspec(f, cell->getPort("\\A"));
- f << stringf(";\n");
+ if (!do_inline)
+ {
+ f << stringf(";\n");
+ }
+ else
+ {
+ f << stringf(")");
+ }
return true;
}
@@ -1243,7 +1369,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
{
if (cell->type[0] == '$' && !noexpr) {
- if (dump_cell_expr(f, indent, cell))
+ if (dump_cell_expr(f, indent, cell, false))
return;
}
@@ -1400,25 +1526,42 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw
void case_body_find_regs(RTLIL::CaseRule *cs)
{
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
- for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
- case_body_find_regs(*it2);
+ {
+ for (auto &c : (*it)->signal.chunks())
+ if (c.wire != NULL && c.offset == 0 && c.width == c.wire->width)
+ proc_consumed_wires[c.wire]++;
+ for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
+ case_body_find_regs(*it2);
+ }
- for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it)
+ {
for (auto &c : it->first.chunks())
+ {
if (c.wire != NULL)
reg_wires.insert(c.wire->name);
+ for (auto &c : it->second.chunks())
+ if (c.wire != NULL && c.offset == 0 && c.width == c.wire->width)
+ proc_consumed_wires[c.wire]++;
+ }
}
}
-void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool find_regs = false)
+void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool sweep = false)
{
- if (find_regs) {
+ if (sweep) {
case_body_find_regs(&proc->root_case);
for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it)
- for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) {
- for (auto &c : it2->first.chunks())
- if (c.wire != NULL)
- reg_wires.insert(c.wire->name);
+ {
+ for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++)
+ {
+ for (auto &c : it2->first.chunks())
+ if (c.wire != NULL)
+ reg_wires.insert(c.wire->name);
+ for (auto &c : it2->second.chunks())
+ if (c.wire != NULL && c.offset == 0 && c.width == c.wire->width)
+ proc_consumed_wires[c.wire]++;
+ }
}
return;
}
@@ -1487,9 +1630,11 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
{
reg_wires.clear();
+ proc_consumed_wires.clear();
reset_auto_counter(module);
active_module = module;
active_sigmap.set(module);
+ active_modindex = ModIndex(module);
active_initdata.clear();
for (auto wire : module->wires())
@@ -1575,6 +1720,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
f << stringf("%s" "endmodule\n", indent.c_str());
active_module = NULL;
+ active_modindex = ModIndex();
active_sigmap.clear();
active_initdata.clear();
}
@@ -1605,7 +1751,12 @@ struct VerilogBackend : public Backend {
log("\n");
log(" -noexpr\n");
log(" without this option all internal cells are converted to Verilog\n");
- log(" expressions.\n");
+ log(" expressions. implies -noinline.\n");
+ log("\n");
+ log(" -noinline\n");
+ log(" without this option all internal cells driving a wire connected to\n");
+ log(" a single internal cell are inlined into that cell and the wire is\n");
+ log(" omitted.\n");
log("\n");
log(" -siminit\n");
log(" add initial statements with hierarchical refs to initialize FFs when\n");
@@ -1662,6 +1813,7 @@ struct VerilogBackend : public Backend {
noattr = false;
attr2comment = false;
noexpr = false;
+ noinline = false;
nodec = false;
nohex = false;
nostr = false;
@@ -1723,7 +1875,11 @@ struct VerilogBackend : public Backend {
continue;
}
if (arg == "-noexpr") {
- noexpr = true;
+ noexpr = noinline = true;
+ continue;
+ }
+ if (arg == "-noinline") {
+ noinline = true;
continue;
}
if (arg == "-nodec") {
diff --git a/kernel/modtools.h b/kernel/modtools.h
index 409562eb..b198709d 100644
--- a/kernel/modtools.h
+++ b/kernel/modtools.h
@@ -226,6 +226,10 @@ struct ModIndex : public RTLIL::Monitor
auto_reload_module = true;
}
+ ModIndex() : module(NULL)
+ {
+ }
+
ModIndex(RTLIL::Module *_m) : sigmap(_m), module(_m)
{
auto_reload_counter = 0;
@@ -235,7 +239,8 @@ struct ModIndex : public RTLIL::Monitor
~ModIndex()
{
- module->monitors.erase(this);
+ if (module)
+ module->monitors.erase(this);
}
SigBitInfo *query(RTLIL::SigBit bit)

View File

@ -1,25 +1,11 @@
{ pkgs, hx }: { pkgs, hx }:
let pkgs.runCommand "helloworld-bitstream" {
symbiflowInput = pkgs.runCommand "helloworld-symbiflow-input" { buildInputs = [ (pkgs.python3.withPackages(ps: [hx.nmigen hx.nmigen-boards hx.heavycomps])) pkgs.yosys ];
buildInputs = [ (pkgs.python3.withPackages(ps: [hx.drvs.nmigen hx.drvs.heavycomps])) hx.drvs.yosys ];
} }
'' ''
mkdir $out export YOSYS=${pkgs.yosys}/bin/yosys
export NEXTPNR_ECP5=${pkgs.nextpnr}/bin/nextpnr-ecp5
python ${./helloworld_ecp5.py} > $out/top.il export ECPPACK=${pkgs.trellis}/bin/ecppack
python ${./helloworld_ecp5.py} $out
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 --speed 8 --package CABGA381" > $out/device
'';
in
hx.lib.symbiflow.buildBitstream {
name = "helloworld-bitstream";
src = symbiflowInput;
}

View File

@ -1,5 +1,7 @@
import argparse
from nmigen import * from nmigen import *
from nmigen.back import rtlil from nmigen_boards.versa_ecp5 import VersaECP5Platform
from heavycomps import uart from heavycomps import uart
@ -7,15 +9,13 @@ from heavycomps import uart
class Top(Elaboratable): class Top(Elaboratable):
def __init__(self, baudrate=115200): def __init__(self, baudrate=115200):
self.baudrate = baudrate self.baudrate = baudrate
self.clk100 = Signal()
self.serial_tx = Signal()
def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
cd_sync = ClockDomain(reset_less=True) cd_sync = ClockDomain(reset_less=True)
m.domains += cd_sync m.domains += cd_sync
m.d.comb += cd_sync.clk.eq(self.clk100) m.d.comb += cd_sync.clk.eq(platform.request("clk100").i)
string = "Hello World!\r\n" string = "Hello World!\r\n"
mem = Memory(width=8, depth=len(string), mem = Memory(width=8, depth=len(string),
@ -29,7 +29,7 @@ class Top(Elaboratable):
m.d.comb += [ m.d.comb += [
tx.stb.eq(~wait), tx.stb.eq(~wait),
tx.data.eq(rdport.data), tx.data.eq(rdport.data),
self.serial_tx.eq(tx.tx) platform.request("uart").tx.o.eq(tx.tx)
] ]
release = Signal() release = Signal()
@ -49,10 +49,10 @@ class Top(Elaboratable):
def main(): def main():
top = Top() parser = argparse.ArgumentParser()
output = rtlil.convert(Fragment.get(top, None), parser.add_argument("build_dir")
ports=(top.clk100, top.serial_tx)) args = parser.parse_args()
print(output) VersaECP5Platform().build(Top(), build_dir=args.build_dir)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -1,47 +1,9 @@
{ pkgs, hx }: { pkgs, hx }:
let pkgs.runCommand "helloworld-bitstream" {
vivadoInput = pkgs.runCommand "helloworld-vivado-input" { buildInputs = [ (pkgs.python3.withPackages(ps: [hx.nmigen hx.nmigen-boards hx.heavycomps])) pkgs.yosys ];
buildInputs = [ (pkgs.python3.withPackages(ps: [hx.drvs.nmigen hx.drvs.heavycomps])) hx.drvs.yosys ];
} }
'' ''
mkdir $out export VIVADO=${hx.vivado}/bin/vivado
python ${./helloworld_kintex7.py} $out
python ${./helloworld_kintex7.py} > $out/top.v ''
cat > $out/top.xdc << EOF
set_property LOC K24 [get_ports serial_tx]
set_property IOSTANDARD LVCMOS25 [get_ports serial_tx]
set_property LOC K28 [get_ports clk156_p]
set_property IOSTANDARD LVDS_25 [get_ports clk156_p]
set_property DIFF_TERM TRUE [get_ports clk156_p]
set_property LOC K29 [get_ports clk156_n]
set_property IOSTANDARD LVDS_25 [get_ports clk156_n]
set_property DIFF_TERM TRUE [get_ports clk156_n]
create_clock -name clk156 -period 6.4 [get_nets clk156_p]
EOF
cat > $out/top.tcl << EOF
create_project -force -name top -part xc7k325t-ffg900-2
set_property XPM_LIBRARIES {XPM_CDC XPM_MEMORY} [current_project]
add_files {top.v}
set_property library work [get_files {top.v}]
read_xdc top.xdc
synth_design -top top -part xc7k325t-ffg900-2
opt_design
place_design
route_design
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.GENERAL.COMPRESS True [current_design]
write_bitstream -force top.bit
quit
EOF
'';
in
hx.lib.vivado.buildBitstream {
name = "helloworld-bitstream";
src = vivadoInput;
}

View File

@ -1,5 +1,7 @@
import argparse
from nmigen import * from nmigen import *
from nmigen.back import verilog from nmigen_boards.kc705 import KC705Platform
from heavycomps import uart from heavycomps import uart
@ -16,8 +18,8 @@ class Top(Elaboratable):
cd_sync = ClockDomain(reset_less=True) cd_sync = ClockDomain(reset_less=True)
m.domains += cd_sync m.domains += cd_sync
m.submodules.clock = Instance("IBUFGDS", m.submodules.clock = Instance("BUFG",
i_I=self.clk156_p, i_IB=self.clk156_n, o_O=cd_sync.clk) i_I=platform.request("clk156").i, o_O=cd_sync.clk)
string = "Hello World!\r\n" string = "Hello World!\r\n"
mem = Memory(width=8, depth=len(string), mem = Memory(width=8, depth=len(string),
@ -29,7 +31,7 @@ class Top(Elaboratable):
m.d.comb += [ m.d.comb += [
tx.stb.eq(1), tx.stb.eq(1),
tx.data.eq(rdport.data), tx.data.eq(rdport.data),
self.serial_tx.eq(tx.tx) platform.request("uart").tx.o.eq(tx.tx)
] ]
with m.If(tx.ack): with m.If(tx.ack):
@ -42,10 +44,10 @@ class Top(Elaboratable):
def main(): def main():
top = Top() parser = argparse.ArgumentParser()
output = verilog.convert(Fragment.get(top, None), parser.add_argument("build_dir")
ports=(top.clk156_p, top.clk156_n, top.serial_tx)) args = parser.parse_args()
print(output) KC705Platform().build(Top(), build_dir=args.build_dir)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -1,27 +1,11 @@
{ pkgs, hx }: { pkgs, hx }:
let pkgs.runCommand "simplesoc-bitstream" {
symbiflowInput = pkgs.runCommand "simplesoc-symbiflow-input" { buildInputs = [ (pkgs.python3.withPackages(ps: [hx.nmigen hx.nmigen-boards hx.heavycomps hx.minerva])) pkgs.yosys ];
buildInputs = [ (pkgs.python3.withPackages(ps: [hx.drvs.nmigen hx.drvs.heavycomps hx.drvs.minerva])) hx.drvs.yosys ];
} }
'' ''
mkdir $out export YOSYS=${pkgs.yosys}/bin/yosys
export NEXTPNR_ECP5=${pkgs.nextpnr}/bin/nextpnr-ecp5
python ${./simplesoc_ecp5.py} ${hx.drvs.fw-helloworld}/helloworld.bin $out/top.il export ECPPACK=${pkgs.trellis}/bin/ecppack
python ${./simplesoc_ecp5.py} ${hx.fw-helloworld}/helloworld.bin $out
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;
LOCATE COMP "led" SITE "E16";
IOBUF PORT "led" IO_TYPE=LVCMOS25;
EOF
echo -n "--um-45k --speed 8 --package CABGA381" > $out/device
'';
in
hx.lib.symbiflow.buildBitstream {
name = "simplesoc-bitstream";
src = symbiflowInput;
}

View File

@ -1,8 +1,10 @@
import os
import argparse import argparse
import struct import struct
from nmigen import * from nmigen import *
from nmigen.back import rtlil, pysim from nmigen.back import pysim
from nmigen_boards.versa_ecp5 import VersaECP5Platform
from heavycomps import uart, wishbone from heavycomps import uart, wishbone
from minerva.core import Minerva from minerva.core import Minerva
@ -27,28 +29,30 @@ class SimpleWishboneSerial(Elaboratable):
class Top(Elaboratable): class Top(Elaboratable):
def __init__(self, firmware, create_clock): def __init__(self, firmware, simulate):
if create_clock:
self.clk100 = Signal()
self.led = Signal()
self.serial_tx = Signal()
self.firmware = firmware self.firmware = firmware
self.simulate = simulate
def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
if hasattr(self, "clk100"): if self.simulate:
io_user_led = Signal()
io_uart_tx = Signal()
else:
cd_sync = ClockDomain(reset_less=True) cd_sync = ClockDomain(reset_less=True)
m.domains += cd_sync m.domains += cd_sync
m.d.comb += cd_sync.clk.eq(self.clk100) m.d.comb += cd_sync.clk.eq(platform.request("clk100").i)
io_user_led = platform.request("user_led").o
io_uart_tx = platform.request("uart").tx.o
counter = Signal(27) counter = Signal(27)
m.d.sync += counter.eq(counter + 1) m.d.sync += counter.eq(counter + 1)
m.d.comb += self.led.eq(counter[-1]) m.d.comb += io_user_led.eq(counter[-1])
m.submodules.cpu = cpu = Minerva(with_icache=False, with_dcache=False, with_muldiv=False) m.submodules.cpu = cpu = Minerva(with_icache=False, with_dcache=False, with_muldiv=False)
m.submodules.ram = ram = wishbone.SRAM(Memory(32, 1024, init=self.firmware)) m.submodules.ram = ram = wishbone.SRAM(Memory(32, 1024, init=self.firmware))
m.submodules.uart = uart = SimpleWishboneSerial(self.serial_tx, 100e6) m.submodules.uart = uart = SimpleWishboneSerial(io_uart_tx, 100e6)
m.submodules.con = con = wishbone.InterconnectShared( m.submodules.con = con = wishbone.InterconnectShared(
[cpu.ibus, cpu.dbus], [cpu.ibus, cpu.dbus],
[ [
@ -74,23 +78,22 @@ def main():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--simulate", action="store_true") parser.add_argument("--simulate", action="store_true")
parser.add_argument("firmware_bin") parser.add_argument("firmware_bin")
parser.add_argument("output_file") parser.add_argument("build_dir")
args = parser.parse_args() args = parser.parse_args()
firmware = read_firmware(args.firmware_bin) firmware = read_firmware(args.firmware_bin)
top = Top(firmware, create_clock=not args.simulate) top = Top(firmware, args.simulate)
if args.simulate: if args.simulate:
os.makedirs(args.build_dir, exist_ok=True)
with pysim.Simulator(top, with pysim.Simulator(top,
vcd_file=open(args.output_file + ".vcd", "w"), vcd_file=open(os.path.join(args.build_dir, "simplesoc.vcd"), "w"),
gtkw_file=open(args.output_file + ".gtkw", "w")) as sim: gtkw_file=open(os.path.join(args.build_dir, "simplesoc.gtkw"), "w")) as sim:
sim.add_clock(1e-6) sim.add_clock(1e-6)
sim.run_until(1000e-6, run_passive=True) sim.run_until(1000e-6, run_passive=True)
else: else:
output = rtlil.convert(Fragment.get(top, None), VersaECP5Platform().build(top, build_dir=args.build_dir)
ports=(top.clk100, top.led, top.serial_tx))
with open(args.output_file, "w") as f:
f.write(output)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -2,7 +2,7 @@
let let
pkgs = import <nixpkgs> { overlays = [ (import ./overlay.nix) ]; }; pkgs = import <nixpkgs> { overlays = [ (import ./overlay.nix) ]; };
hx = import ./default.nix { inherit pkgs; }; hx = import ./default.nix { inherit pkgs; };
jobs = hx.drvs // { jobs = hx // {
inherit (pkgs) llvm_7 rustc cargo cargo-vendor; inherit (pkgs) llvm_7 rustc cargo cargo-vendor;
helloworld_ecp5 = import ./examples/helloworld_ecp5.nix { inherit pkgs hx; }; helloworld_ecp5 = import ./examples/helloworld_ecp5.nix { inherit pkgs hx; };