*: add qasm, revamp bsc wrap magic, use nix cc toolchain

main
q3k 2021-10-15 01:44:27 +02:00
parent 1c37f0c695
commit cfa97935c0
14 changed files with 438 additions and 16443 deletions

View File

@ -1,3 +1,6 @@
build --host_platform=@io_tweag_rules_nixpkgs//nixpkgs/platforms:host
build --incompatible_use_cc_configure_from_rules_cc
build --host_crosstool_top=@local_config_cc//:toolchain
build --platforms=//build/platforms:ulx3s_85f
build --experimental_allow_unresolved_symlinks
build --host_cxxopt='-std=c++17'

View File

@ -4,6 +4,12 @@ workspace(
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_cc",
urls = ["https://github.com/bazelbuild/rules_cc/releases/download/0.0.1/rules_cc-0.0.1.tar.gz"],
sha256 = "4dccbfd22c0def164c8f47458bd50e0c7148f3d92002cdb459c2a96a68498241",
)
http_archive(
name = "io_tweag_rules_nixpkgs",
strip_prefix = "rules_nixpkgs-81f61c4b5afcf50665b7073f7fce4c1755b4b9a3",
@ -14,30 +20,55 @@ http_archive(
load("@io_tweag_rules_nixpkgs//nixpkgs:repositories.bzl", "rules_nixpkgs_dependencies")
rules_nixpkgs_dependencies()
load("@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl", "nixpkgs_git_repository", "nixpkgs_package")
load("@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl", "nixpkgs_git_repository", "nixpkgs_package", "nixpkgs_cc_configure")
nixpkgs_git_repository(
name = "nixpkgs",
revision = "ea25862403b62189b0e4256d1a17ed611f0d88bf",
sha256 = "2a7a0e10461382470b1196f60d0ab173d090d0030526f517b1716e9cf318ef14",
revision = "71ec7f4ad20b5c4c4a14d2a09f6040ade87c257d",
sha256 = "f712f0586a3ffa693741ca1e62f26d366cc0f663041066a5b8f5606e930ff8b2",
)
nixpkgs_cc_configure(
name = "local_config_cc",
repository = "@nixpkgs//:default.nix",
)
nixpkgs_package(
name = "bluespec",
repositories = { "nixpkgs": "@nixpkgs//:default.nix" },
build_file_content = """
load("@qfc//build:utils.bzl", "external_binary_tool")
load("@qfc//build/bluespec:rules.bzl", "bluespec_toolchain")
external_binary_tool(
sh_binary(
name = "bsc",
bin = "bin/bsc",
deps = glob(["lib/**", "bin/core/**"]),
data = [
"bin/bsc"
] + glob(["lib/**", "bin/core/**"]),
srcs = [
"@qfc//build/bluespec:bsc.sh",
],
deps = [
"@bazel_tools//tools/bash/runfiles",
],
)
sh_binary(
name = "bluetcl",
data = [
"bin/bluetcl"
] + glob(["lib/**", "bin/core/**"]),
srcs = [
"@qfc//build/bluespec:bluetcl.sh",
],
deps = [
"@bazel_tools//tools/bash/runfiles",
],
)
bluespec_toolchain(
name = "bsc_nixpkgs",
bsc = ":bsc",
bluetcl = ":bluetcl",
verilog_lib = glob(["lib/Verilog/*.v"], [
"lib/Verilog/BRAM2.v",
"lib/Verilog/BRAM2Load.v",
@ -64,9 +95,9 @@ toolchain(
nixpkgs_package(
name = "yosysflow",
repositories = { "nixpkgs1": "@nixpkgs//:default.nix" },
repositories = { "nixpkgs2": "@nixpkgs//:default.nix" },
nix_file_content = """
with import <nixpkgs1> {}; symlinkJoin {
with import <nixpkgs2> {}; symlinkJoin {
name = "yosysflow";
paths = with pkgs; [ yosys nextpnr trellis ];
}

View File

@ -14,7 +14,18 @@ bluespec_library(
"Board": ["mkTop", "mkMemory"],
},
data = [
"bram.bin",
":bram",
],
)
genrule(
name = "bram",
tools = [
"//lanai/qasm",
],
cmd = "$(location //lanai/qasm) $@",
outs = [
"bram.bin"
],
)

View File

@ -21,7 +21,8 @@ endinterface
(* synthesize *)
module mkMemory(Lanai_Memory#(1024));
Lanai_Memory#(1024) inner <- mkBlockMemory("boards/ulx3s/bram.bin");
// TODO(q3k): ... figure out how the fuck to unhardcode this.
Lanai_Memory#(1024) inner <- mkBlockMemory("bazel-out/k8-fastbuild/bin/boards/ulx3s/bram.bin");
interface dmem = inner.dmem;
interface imem = inner.imem;
endmodule

File diff suppressed because it is too large Load Diff

View File

@ -11,3 +11,5 @@ cc_binary(
],
visibility = [ "//visibility:public" ],
)
exports_files(["bluetcl.sh", "bsc.sh"])

View File

@ -10,11 +10,40 @@
#include <string>
int main(int argc, char **argv) {
auto bsc = std::getenv("BSC");
if (bsc == nullptr) {
std::cerr << "BSC must be set\n";
char **new_argv = new char*[argc];
int new_argv_ix = 0;
char *bsc = nullptr;
char *cxx = nullptr;
char *strip = nullptr;
for (int i = 1; i < argc; i++) {
if (new_argv_ix > 0) {
new_argv[new_argv_ix++] = argv[i];
} else {
if (std::string(argv[i]) == "--") {
new_argv_ix = 1;
} else {
switch (i) {
case 1:
bsc = argv[1];
break;
case 2:
cxx = argv[i];
break;
case 3:
strip = argv[i];
break;
}
}
}
}
if (bsc == nullptr || cxx == nullptr || strip == nullptr) {
std::cerr << "Usage: " << argv[0] << " bsc cxx strip -- bsc args\n";
return 1;
}
new_argv[0] = bsc;
new_argv[new_argv_ix] = nullptr;
auto tmp = std::getenv("TMPDIR");
if (tmp == nullptr) {
@ -27,18 +56,8 @@ int main(int argc, char **argv) {
std::filesystem::remove_all(forest);
std::filesystem::create_directory(forest);
std::map<std::string, std::string> links = {
{"c++", "CXX"},
{"strip", "STRIP"},
};
for (auto const& [k, v] : links) {
auto target = std::getenv(v.c_str());
if (target == nullptr) {
std::cerr << v << " must be set\n";
return 1;
}
std::filesystem::create_symlink(target, forest + "/" + k);
}
std::filesystem::create_symlink(cxx, forest + "/c++");
std::filesystem::create_symlink(strip, forest + "/strip");
auto path = forest + ":" + (std::getenv("PATH") ? std::getenv("PATH") : "");
@ -47,7 +66,7 @@ int main(int argc, char **argv) {
nullptr,
};
if (execvpe(bsc, argv, envp) == -1) {
if (execvpe(bsc, new_argv, envp) == -1) {
std::cerr << "execvpe failed\n";
return 1;
}

View File

@ -5,6 +5,7 @@ def _bluespec_toolchain_impl(ctx):
toolchain_info = platform_common.ToolchainInfo(
bscinfo = BscInfo(
bsc = ctx.attr.bsc,
bluetcl = ctx.attr.bluetcl,
verilog_lib = VerilogInfo(
sources = depset(ctx.files.verilog_lib),
),
@ -19,6 +20,10 @@ bluespec_toolchain = rule(
executable = True,
cfg = "exec",
),
"bluetcl": attr.label(
executable = True,
cfg = "exec",
),
"verilog_lib": attr.label_list(
allow_files = True,
),
@ -27,7 +32,6 @@ bluespec_toolchain = rule(
def _compile(ctx, src, dep_objs, output, mode, verilog_outputs=[], sim_outputs=[]):
info = ctx.toolchains["@qfc//build/bluespec:toolchain_type"].bscinfo
bsc = info.bsc[DefaultInfo].files_to_run
bdir = output.dirname
pkg_path_set = {}
@ -67,18 +71,23 @@ def _compile(ctx, src, dep_objs, output, mode, verilog_outputs=[], sim_outputs=[
src.path,
]
_, _, input_manifests = ctx.resolve_command(tools = [info.bsc])
ctx.actions.run(
mnemonic = mnemonic,
executable = bsc,
executable = info.bsc.files_to_run,
arguments = arguments,
inputs = depset([ src ], transitive=[dep_objs]),
outputs = sim_outputs + verilog_outputs + [ output ],
input_manifests = input_manifests,
use_default_shell_env = True,
env = {
"PATH": ctx.configuration.default_shell_env.get('PATH', ""),
},
)
def _library_inner(ctx):
info = ctx.toolchains["@qfc//build/bluespec:toolchain_type"].bscinfo
bsc = info.bsc[DefaultInfo].files_to_run
package_order = []
package_to_src = {}
@ -247,6 +256,7 @@ def _bluesim_test_impl(ctx):
info = ctx.toolchains["@qfc//build/bluespec:toolchain_type"].bscinfo
cc_toolchain = find_cpp_toolchain(ctx)
bsc = info.bsc
bluetcl = info.bluetcl
sim_objs = depset(
[],
@ -264,7 +274,7 @@ def _bluesim_test_impl(ctx):
] + [
dep[BluespecInfo].data_files
for dep in ctx.attr.deps
],
] + [ bluetcl.default_runfiles.files ],
)
pkg_path_set = {}
@ -283,18 +293,22 @@ def _bluesim_test_impl(ctx):
# information into bsc somehow. Maybe using a real linker?
if cxx.endswith('/gcc'):
cxx = cxx[:-2] + "++"
elif cxx.endswith('/cc'):
cxx = cxx[:-2] + "g++"
ctx.actions.run(
mnemonic = "BluespecSimLink",
executable = ctx.executable._bscwrap,
tools = [
bsc[DefaultInfo].files_to_run,
bsc.files_to_run
],
inputs = depset(
[],
transitive = [ bsc.files, sim_objs, sim_outputs ],
transitive = [ sim_objs, sim_outputs ],
),
arguments = [
bsc.files_to_run.executable.path, cxx, cc_toolchain.strip_executable,
"--",
"-p", ":".join(pkg_path),
"-sim",
"-simdir", test.dirname,
@ -302,29 +316,36 @@ def _bluesim_test_impl(ctx):
"-e",
ctx.attr.top,
],
env = {
"CXX": cxx,
"STRIP": cc_toolchain.strip_executable,
"BSC": bsc[DefaultInfo].files_to_run.executable.path,
"PATH": ctx.configuration.default_shell_env.get('PATH', ""),
},
use_default_shell_env = True,
outputs = [test, testSo]
)
wrapper = ctx.actions.declare_file(ctx.attr.name + ".wrap")
test_path = ctx.workspace_name + "/" + test.short_path
ctx.actions.write(
output = wrapper,
content = """#!/bin/sh
t="{}"
$t $@ >res 2>&1
cat res
if grep -q "Error:" res ; then
exit 1
fi
if grep -q "Dynamic assertion failed:" res ; then
exit 1
fi
""".format(test.short_path),
content = """#!/usr/bin/env bash
# --- begin runfiles.bash initialization v2 ---
# Copy-pasted from the Bazel Bash runfiles library v2.
set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \\
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \\
source "$0.runfiles/$f" 2>/dev/null || \\
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \\
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \\
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
# --- end runfiles.bash initialization v2 --- """ + ("""
export PATH="$(dirname $(rlocation bluespec/bluetcl)):$PATH"
t="$(rlocation {})"
$t $@ >res 2>&1
cat res
if grep -q "Error:" res ; then
exit 1
fi
if grep -q "Dynamic assertion failed:" res ; then
exit 1
fi
""".format(test_path)),
is_executable = True,
)

View File

@ -2,6 +2,7 @@
BscInfo = provider(
fields = [
"bsc",
"bluetcl",
"verilog_lib",
],
)

View File

@ -1,16 +1,12 @@
def _external_binary_tool_impl(ctx):
bin_ = ctx.file.bin
executable = ctx.actions.declare_file(ctx.attr.name + ".bin")
path = ctx.file.bin.path
strip = "external/"
if not path.startswith(strip):
fail("Unexpected path {}".format(path))
path = path[len(strip):]
path = executable.path + ".runfiles/" + ctx.workspace_name + "/" + ctx.file.bin.short_path
ctx.actions.write(
output = executable,
content = """#!/bin/sh
sp="{}"
bin=$0.runfiles/$sp
exec $bin $@
exec "{}" $@
""".format(path),
is_executable = True,
)

View File

@ -35,7 +35,18 @@ bluespec_library(
":Lanai",
],
data = [
"bram.bin",
":bram",
],
)
genrule(
name = "bram",
tools = [
"//lanai/qasm",
],
cmd = "$(location //lanai/qasm) $@",
outs = [
"bram.bin"
],
)

File diff suppressed because it is too large Load Diff

9
lanai/qasm/BUILD.bazel Normal file
View File

@ -0,0 +1,9 @@
py_binary(
name = "qasm",
srcs = [
"qasm.py",
],
visibility = [
"//visibility:public",
],
)

274
lanai/qasm/qasm.py Normal file
View File

@ -0,0 +1,274 @@
# qasm - the quaint lanai assembler.
#
# This is meant to be used for testing only.
import math
import struct
def serialize(l):
bs = []
for (nbits, val) in l:
val = int(val)
if val > 0 and math.log(val, 2) > nbits:
raise Exception('too wide, {} bits, {}', nbits, val)
bs.append(('{:0'+str(nbits)+'b}').format(val))
return '_'.join(bs)
def op3bit(opcode):
return {
'add': 0, 'addc': 1, 'sub': 2, 'subb': 3, 'and': 4, 'or': 5, 'xor': 6,
'sh': 7, 'sha': 7,
}[opcode]
def op7bit(opcode):
if opcode == 'sh':
return 0b11110000
if opcode == 'sha':
return 0b11111000
if opcode == 'sel':
return 0b11100000
return op3bit(opcode) << 5
def ri(opcode, rd, rs, flags, const):
high = False
if opcode in ['sh', 'sha']:
if const > 31 or const < -31:
raise Exception('invalid shift amount')
const, = struct.unpack('>H', struct.pack('>h', const))
high = opcode == 'sha'
else:
if (const & 0xffff) == 0:
high = True
const = const >> 16
else:
if (const >> 16) != 0:
raise Exception("invalid constant {:032x}".format(const))
return serialize([
(1, 0),
(3, op3bit(opcode)),
(5, rd),
(5, rs),
(1, flags),
(1, high),
(16, const),
])
def rr(opcode, rd, rs1, flags, rs2, condition):
return serialize([
(4, 0b1100),
(5, rd),
(5, rs1),
(1, flags),
(1, condition & 1),
(5, rs2),
(8, op7bit(opcode)),
(3, condition >> 1),
])
def rm(store, rd, rs1, p, q, constant):
return serialize([
(3, 0b100),
(1, store),
(5, rd),
(5, rs1),
(1, p),
(1, q),
(16, constant),
])
def regno(name):
val = {
'pc': 2, 'sw': 3, 'rv': 8, 'rr1': 10, 'rr2': 11,
}.get(name)
if val is not None:
return val
return int(name[1:])
def condno(name):
return {
't': 0, 'f': 1, 'hi': 2, 'ls': 3, 'cc': 4, 'cs': 5, 'ne': 6, 'eq': 7,
'vc': 8, 'vs': 9, 'pl': 10, 'mi': 11, 'ge': 12, 'lt': 13, 'gt': 14,
'le': 15
}[name]
def asm(text):
parts = text.split()
opcode = parts[0]
flags = opcode.endswith('.f')
if flags:
opcode = opcode[:-2]
operands = [p.strip(',') for p in parts[1:]]
registers = [regno(el[1:]) if el.startswith('%') else None for el in operands]
imms = []
for el in operands:
try:
val = int(el)
except ValueError:
try:
val = int(el, 16)
except ValueError:
val = None
imms.append(val)
if opcode in ['add', 'addc', 'sub', 'subb', 'and', 'or', 'xor', 'sh', 'sha']:
if len(operands) != 3:
raise Exception('invalid operand count {}'.format(text))
[src1, src2, dest] = registers
if dest is None:
raise Exception('destination must be register')
if src1 is None:
raise Exception('source must be register')
if src2 is None:
return ri(opcode, dest, src1, flags, imms[1])
else:
return rr(opcode, dest, src1, flags, src2, 0)
if opcode.startswith('sel.'):
cond = opcode.split('.')[1]
[src1, src2, dest] = registers
if dest is None:
raise Exception('destination must be register')
if src1 is None:
raise Exception('source1 must be register')
if src2 is None:
raise Exception('source2 must be register')
return rr('sel', dest, src1, flags, src2, condno(cond))
if opcode in ['ld', 'st']:
if len(operands) != 2:
raise Exception('invalid operand count {}'.format(text))
mo = 0
ro = 1
if opcode == 'st':
mo = 1
ro = 0
parts = operands[mo].split('[')
offs = None
if len(parts) == 1:
offs = 0
else:
offs = parts[0]
parts = parts[1:]
src = parts[0].split(']')[0]
preincr = False
postincr = False
if src.endswith('++'):
postincr = True
offs = 4
src = src[:-2]
if src.endswith('*'):
postincr = True
src = src[:-1]
if src.startswith('++'):
preincr = True
offs = 4
src = src[2:]
if src.startswith('*'):
preincr = True
src = src[1:]
dest = registers[ro]
offs = int(offs)
src = regno(src[1:])
p = False
q = False
if offs != 0:
if preincr:
p = True
q = True
elif postincr:
q = True
else:
p = True
return rm(opcode == 'st', dest, src, p, q, offs)
max_counter = int(25e6/7)
#max_counter = 5
insns = {
#0: 'add %r0, {}, %r16'.format(max_counter & 0xffff),
#4: 'or %r16, {}, %r16'.format(max_counter & 0xffff0000),
#8: 'add %r0, 0, %r7',
#12: 'add %r0, 0, %r4',
##16: 'add %r0, 0xf000, %r9',
##32: 'ld 0[%r9], %r14',
##36: 'ld [%r9++], %r14',
##40: 'ld 4[%r9], %r14',
##44: 'ld [++%r9], %r14',
##64: 'st %r14, 0[%r9]',
##68: 'st %r14, [%r9++]',
##72: 'st %r14, 4[%r9]',
##76: 'st %r14, [++%r9]',
#16: 'add %r4, 1, %r4',
#20: 'add %r7, 1, %r17',
#24: 'sub.f %r4, %r16, %r10',
#28: 'sel.pl %r17, %r7, %r7',
#32: 'sel.pl %r0, %r4, %r4',
#36: 'add %r0, 0x10, %pc',
#0: 'add %r0, {}, %r16'.format(max_counter & 0xffff),
#4: 'or %r16, {}, %r16'.format(max_counter & 0xffff0000),
#8: 'add %r0, 0, %r7',
#12: 'add %r0, 0, %r4',
#16: 'add %r4, 1, %r4',
#20: 'add %r7, 1, %r17',
#24: 'sub.f %r4, %r16, %r0',
#28: 'sel.eq %r17, %r7, %r7',
#32: 'sel.eq %r0, %r4, %r4',
#36: 'add %r0, 0x10, %pc',
0: 'add %r0, 0xbee0, %r10',
4: 'add %r0, 256, %r9',
8: 'st %r10, 0[%r9]',
12: 'ld 0[%r9++], %r11',
16: 'add %r11, 1, %r7',
20: 'add %r11, 2, %r7',
24: 'add %r11, 3, %r7',
28: 'add %r11, 4, %r7',
60: 'add %r0, 0, %r11',
64: 'add %r0, 512, %r9',
68: 'st %r10, 0[%r9]',
72: 'ld 0[%r9++], %r11',
76: 'add %r11, 1, %r7',
80: 'add %r11, 2, %r7',
84: 'add %r11, 3, %r7',
88: 'add %r11, 4, %r7',
256: 'add %r0, 128, %pc',
}
ram = [0 for i in range(2**15)]
for addr, ins in insns.items():
ins = int(asm(ins).replace('_', ''), 2)
ram[addr+0] = (ins >> 24 ) & 0xff
ram[addr+1] = (ins >> 16 ) & 0xff
ram[addr+2] = (ins >> 8 ) & 0xff
ram[addr+3] = (ins >> 0 ) & 0xff
import sys
with open(sys.argv[1], 'w') as f:
for i in range(0, len(ram), 4):
el = ram[i+0] << 24
el |= ram[i+1] << 16
el |= ram[i+2] << 8
el |= ram[i+3]
f.write('{:08x}\n'.format(el))
#with open('bram.bin', 'wb') as f:
# for i in range(0, len(ram)):
# f.write(bytes([ram[i]]))