Compare commits

..

6 Commits

Author SHA1 Message Date
David Anderson 77b772c7ee tasks.py: improve handling of Bluespec libdirs
To help divvy up the sources better, the build now makes a libpath
consisting of all directories that have bsv files in them, with a few
exceptions: hardware subdirs are target-specific so only get used if
they're the current build target. Experiments are random crap so get
the same treatment. And the 'sim' dir is only test helpers, so they
only get pulled in by tests.
2024-09-07 10:31:32 -07:00
David Anderson 25d1806590 lib/ClockOut: hack module to export a clock as an ordinary signal
Used to output a clock signal from an FPGA pin. The resultant output
signal is unclocked, so can be presented to any output at will.
2024-09-07 10:06:50 -07:00
David Anderson 5e997201db hardware/sentinel65x: move top-level hw module to hw subdir 2024-09-07 10:04:36 -07:00
David Anderson 6fd040565c Grab the inout port fixer from bsc tree, wire it in
Yosys doesn't understand Verilog-2001 port aliases. Unfortunately
bsc uses those to represent inout ports because it's the only way
to represent a particular kind of shared bus in Verilog source code.

Thankfully, a kind soul at Bluespec Inc made a perl script that
transforms the port alias construct into regular verilog-1995, which
works fine in cases like mine where the only user of the inout port
is a TriState module which tears it apart into separate
input/output/enable signals for the rest of bsc to work with.
2024-09-06 21:26:39 -07:00
David Anderson 0005ad6fe5 sentinel65x/PLL: update PLL settings, fix generator 2024-09-06 21:18:00 -07:00
David Anderson d960eee973 flake.lock: update tools 2024-09-06 21:17:32 -07:00
8 changed files with 219 additions and 16 deletions

View File

@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1723175592, "lastModified": 1725432240,
"narHash": "sha256-M0xJ3FbDUc4fRZ84dPGx5VvgFsOzds77KiBMW/mMTnI=", "narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "5e0ca22929f3342b19569b21b2f3462f053e497b", "rev": "ad416d066ca1222956472ab7d0555a6946746a80",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -1,6 +1,6 @@
package PLL; package PLL;
// Frequencies: FPGA 100MHz, CPU 10MHz, VGA 25MHz // Frequencies: FPGA 100MHz, CPU 8MHz, VGA 25MHz
// //
// To regenerate, see 'inv genclk -h' // To regenerate, see 'inv genclk -h'

View File

@ -6,13 +6,13 @@ module PLL
( (
input CLK_REF, // 25 MHz, 0 deg input CLK_REF, // 25 MHz, 0 deg
output CLK_MAIN, // 100 MHz, 0 deg output CLK_MAIN, // 100 MHz, 0 deg
output CLK_CPU, // 10 MHz, 0 deg output CLK_CPU, // 8 MHz, 0 deg
output CLK_VGA, // 25 MHz, 0 deg output CLK_VGA, // 25 MHz, 0 deg
output locked output locked
); );
(* FREQUENCY_PIN_CLKI="25" *) (* FREQUENCY_PIN_CLKI="25" *)
(* FREQUENCY_PIN_CLKOP="100" *) (* FREQUENCY_PIN_CLKOP="100" *)
(* FREQUENCY_PIN_CLKOS="10" *) (* FREQUENCY_PIN_CLKOS="8" *)
(* FREQUENCY_PIN_CLKOS2="25" *) (* FREQUENCY_PIN_CLKOS2="25" *)
(* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *) (* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
EHXPLLL #( EHXPLLL #(
@ -30,7 +30,7 @@ EHXPLLL #(
.CLKOP_CPHASE(2), .CLKOP_CPHASE(2),
.CLKOP_FPHASE(0), .CLKOP_FPHASE(0),
.CLKOS_ENABLE("ENABLED"), .CLKOS_ENABLE("ENABLED"),
.CLKOS_DIV(60), .CLKOS_DIV(75),
.CLKOS_CPHASE(2), .CLKOS_CPHASE(2),
.CLKOS_FPHASE(0), .CLKOS_FPHASE(0),
.CLKOS2_ENABLE("ENABLED"), .CLKOS2_ENABLE("ENABLED"),

View File

@ -0,0 +1,57 @@
package Top;
import Connectable::*;
import TriState::*;
import ClockOut::*;
import PLL::*;
interface SystemBus;
(* always_enabled,prefix="" *)
method Action addr(UInt#(24) addr);
(* always_enabled,prefix="" *)
method Action phi2(bit phi2);
(* always_enabled,prefix="" *)
method Action write(bit we);
endinterface
module mkSystemBus(SystemBus);
endmodule
interface Top;
(* always_ready *)
method bit clkout();
interface SystemBus cpu;
endinterface
(* synthesize,no_default_clock,no_default_reset,default_gate_unused *)
module mkTop(Clock clk_ref,
(* clocked_by="no_clock" *) Reset rst,
(* clocked_by="no_clock" *) Inout#(Bit#(8)) data,
Top ifc);
let pll <- mkPLL(clk_ref);
let cpu_clk <- mkClockOut(clocked_by(pll.cpu_clk), reset_by(rst));
Reg#(Bool) data_out_en <- mkReg(False, clocked_by(pll.cpu_clk), reset_by(rst));
Reg#(Bit#(8)) data_out <- mkReg(0, clocked_by(pll.cpu_clk), reset_by(rst));
TriState#(Bit#(8)) data_in <- mkTriState(data_out_en, data_out, clocked_by(pll.cpu_clk), reset_by(rst));
mkConnection(data, data_in.io);
interface SystemBus cpu;
method clkout = cpu_clk.value;
method Action addr(a);
noAction;
endmethod
method Action phi2(v);
noAction;
endmethod
method Action write(bit we);
noAction;
endmethod
//interface data = data_in.io;
endinterface
endmodule
endpackage

16
lib/ClockOut.bsv Normal file
View File

@ -0,0 +1,16 @@
package ClockOut;
interface ClockOut;
method bit value();
endinterface
import "BVI" ClockOut =
module mkClockOut(ClockOut);
default_reset no_reset;
default_clock clk (CLK, (* unused *)GATE);
method CLK_BIT value() clocked_by(no_clock);
schedule value CF value;
endmodule
endpackage

3
lib/ClockOut.v Normal file
View File

@ -0,0 +1,3 @@
module ClockOut(input CLK, output CLK_BIT);
assign CLK_BIT = CLK;
endmodule

96
scripts/basicinout.pl Executable file
View File

@ -0,0 +1,96 @@
#!/usr/bin/env perl
# -*-Perl-*-
################################################################################
################################################################################
### NOTE ###
#
# This script comes from the Bluespec source repository,
# https://github.com/B-Lang-org/bsc/blob/main/util/scripts/basicinout.pl
#
# Unlike the rest of this repo, it is licensed under BSD-3-Clause like
# the original, with credit and gratitude to Bluespec Inc. and the
# anonymous programmers who wrote it prior to the open-sourcing of
# Bluespec.
#
### NOTE ###
my %RENAME_PORTS = ();
my %SIGNALS = ();
my %PINS = ();
foreach my $outfile (@ARGV) {
# read the file
next unless open(FILE, $outfile);
my @lines = <FILE>;
close(FILE);
# Locate inout signals
my $inmodule = 0;
my $showedassigns = 0;
my @newlines;
foreach my $line (@lines) {
if ($line =~ m/rename\:\s+(\S+)\=(\S+)/) {
$RENAME_PORTS{$1} = $2;
} elsif ($line =~ m/^\s*module\s*[a-zA-Z0-9_\$]+\s*\(\s*\.(\S+)\(([a-zA-Z0-9_\$]+)\)/) {
$inmodule = 1;
$SIGNALS{$2} = $1;
$PINS{$1} = $2;
$line =~ s/\.(\S+)\(([a-zA-Z0-9_\$]+)\)/$1/;
push @newlines, $line;
} elsif ($line =~ m/^\s*module\s+(\S+)\s*\(/) {
$inmodule = 1;
push @newlines, $line;
} elsif ($line =~ m/^\s*\.(\S+)\(([a-zA-Z0-9_\$]+)\)/ && $inmodule) {
$SIGNALS{$2} = $1;
$PINS{$1} = $2;
$line =~ s/\.(\S+)\(([a-zA-Z0-9_\$]+)\)/$1/;
push @newlines, $line;
} elsif ($line =~ m/\s*inout(.*?)\s*(\S+)\;/) {
my $signal = $2;
my $origsig = $2;
if (exists $SIGNALS{$signal}) {
my $pin = $SIGNALS{$signal};
$signal =~ s/\$/\\\$/g;
$line =~ s/$signal/$pin/;
} else {
print("Failed to locate signal=$signal in module port list (basicinout)!\nPlease report this error to the BSC developers, by opening a ticket\nin the issue database\: https\:\/\/github.com\/B-Lang-org\/bsc\/issues\n\n");
die;
}
push @newlines, $line;
} elsif ($line =~ m/input/ && $inmodule) {
$inmodule = 0;
push @newlines, $line;
} elsif ($line =~ m/\.(\S+)\(([a-zA-Z0-9\$_]+)\)/) {
my $signal = $2;
if (exists $SIGNALS{$signal}) {
my $pin = $SIGNALS{$signal};
$signal =~ s/\$/\\\$/g;
$line =~ s/$signal/$pin/;
}
push @newlines, $line;
} else {
push @newlines, $line;
}
}
# Rename any signals that need renaming
my @renamed_lines;
foreach my $line (@newlines) {
foreach my $signal (keys %RENAME_PORTS) {
my $replacement = $RENAME_PORTS{$signal};
if ($line =~ m/$signal/) {
$line =~ s/([A-Za-z0-9_\$]*$signal)/$replacement/g;
}
}
push @renamed_lines, $line;
}
# write out the new version
open(OFILE, ">${outfile}") or die("Could not create output file: $!\n");
print OFILE @renamed_lines;
close(OFILE);
}
1;

View File

@ -24,18 +24,45 @@ def bsc_root(c):
return Path(l[len(dir_prefix):]) return Path(l[len(dir_prefix):])
raise RuntimeError("Couldn't locate Bluespec root dir") raise RuntimeError("Couldn't locate Bluespec root dir")
def find_verilog_modules(c, modules): def bluespec_libdirs(*extras):
libpaths = [Path("lib"), bsc_root(c) / "Verilog"] ret = []
for x in extras:
x = Path(x)
if not x.is_dir():
x = x.parent
if not x.is_dir():
raise ValueError(f"unknown libdir thing {x}")
ret.append(x)
bsv_dirs = list(sorted(set(p.parent for p in Path("").glob("**/*.bsv"))))
exclude_trees = [Path("hardware"), Path("experiments"), Path("sim")] + ret
for d in bsv_dirs:
if any(d.is_relative_to(x) for x in exclude_trees):
continue
ret.append(d)
ret.append("%/Libraries")
return ":".join(str(s) for s in ret)
def find_verilog_modules(c, target_dir, modules):
preferred_libpaths = [target_dir, Path("lib"), bsc_root(c) / "Verilog"]
ret = [] ret = []
for module in modules: for module in modules:
module_path = None module_path = None
verilog_path = Path(module).with_suffix(".v")
# Try preferred libpaths first.
for p in libpaths: for p in libpaths:
f = p / Path(module).with_suffix(".v") f = p / verilog_path
if f.is_file(): if f.is_file():
module_path = f module_path = f
break break
if module_path is None: if module_path is None:
raise RuntimeError(f"Cannot find verilog module {module} in {libpaths}") # Not in preferred paths, accept anywhere now.
matches = Path("").glob(f"**/{module}.v")
if len(matches) > 1:
raise RuntimeError(f"Multiple candidates for verilog module {module}: {matches}")
elif len(matches) == 0:
raise RuntimeError(f"Cannot find verilog module {module} in {libpaths}")
module_path = matches[0]
ret.append(module_path) ret.append(module_path)
return ret return ret
@ -82,14 +109,16 @@ def phase(name):
print("") print("")
@task @task
def build(c, target="."): def build(c, target):
phase("Compile Bluespec") phase("Compile Bluespec")
verilog_files = [] verilog_files = []
for target in expand_build_target(target): for target in expand_build_target(target):
out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc") out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc")
libdirs = bluespec_libdirs(target)
print(f"Building {target}") print(f"Building {target}")
c.run(f"bsc -aggressive-conditions -check-assert -remove-dollar -remove-empty-rules -remove-false-rules -remove-starved-rules -remove-unused-modules -show-method-conf -show-method-bvi -u -verilog -info-dir {out_info} -vdir {out_verilog} -bdir {out_bsc} -p {target.parent}:vram:lib:%/Libraries -show-module-use -show-compiles {target}") print(f"Libdirs: {libdirs.replace(':', ', ')}")
c.run(f"bsc -aggressive-conditions -check-assert -remove-dollar -remove-empty-rules -remove-false-rules -remove-starved-rules -verilog-filter scripts/basicinout.pl -show-method-conf -show-method-bvi -u -verilog -info-dir {out_info} -vdir {out_verilog} -bdir {out_bsc} -p {libdirs} -show-module-use -show-compiles {target}")
module_name = Path(f"mk{target.stem}") module_name = Path(f"mk{target.stem}")
verilog_main_file = out_verilog / module_name.with_suffix(".v") verilog_main_file = out_verilog / module_name.with_suffix(".v")
@ -98,7 +127,7 @@ def build(c, target="."):
use_file = out_verilog / module_name.with_suffix(".use") use_file = out_verilog / module_name.with_suffix(".use")
if use_file.is_file(): if use_file.is_file():
with open(out_verilog / module_name.with_suffix(".use")) as f: with open(out_verilog / module_name.with_suffix(".use")) as f:
verilog_files.extend(find_verilog_modules(c, f.read().splitlines())) verilog_files.extend(find_verilog_modules(c, target.parent, f.read().splitlines()))
if verilog_files: if verilog_files:
print("\nVerilog files for synthesis:") print("\nVerilog files for synthesis:")
@ -187,7 +216,9 @@ def synth(c, target):
def test(c, target): def test(c, target):
for target in expand_test_target(target): for target in expand_test_target(target):
out_info, out_sim, out_bsc = ensure_build_dirs(target, "info", "sim", "bsc") out_info, out_sim, out_bsc = ensure_build_dirs(target, "info", "sim", "bsc")
c.run(f"bsc -show-schedule -aggressive-conditions -check-assert -u -sim -info-dir {out_info} -simdir {out_sim} -bdir {out_bsc} -g mkTB -p {target.parent}:lib:sim:%/Libraries {target}") libdirs = bluespec_libdirs(target, "sim")
print(f"Libdirs: {libdirs.replace(':', ', ')}")
c.run(f"bsc -show-schedule -aggressive-conditions -check-assert -u -sim -info-dir {out_info} -simdir {out_sim} -bdir {out_bsc} -g mkTB -p {libdirs} {target}")
exec = out_sim / "TB" exec = out_sim / "TB"
c.run(f"bsc -p {out_bsc} -sim -simdir {out_sim} -e mkTB -o {exec}") c.run(f"bsc -p {out_bsc} -sim -simdir {out_sim} -e mkTB -o {exec}")
testdata = out_sim / "testdata" testdata = out_sim / "testdata"
@ -205,7 +236,7 @@ def clean(c):
@task @task
def genclk(c, in_mhz=25, main_mhz=100, cpu_mhz=10, vga_mhz=25): def genclk(c, in_mhz=25, main_mhz=100, cpu_mhz=10, vga_mhz=25):
out = Path("gary/PLL.v") out = Path("sentinel65x/PLL.v")
c.run(f"ecppll -f {out} --module PLL --clkin_name CLK_REF --clkin {in_mhz} --clkout0_name CLK_MAIN --clkout0 {main_mhz} --clkout1_name CLK_CPU --clkout1 {cpu_mhz} --clkout2_name CLK_VGA --clkout2 {vga_mhz}") c.run(f"ecppll -f {out} --module PLL --clkin_name CLK_REF --clkin {in_mhz} --clkout0_name CLK_MAIN --clkout0 {main_mhz} --clkout1_name CLK_CPU --clkout1 {cpu_mhz} --clkout2_name CLK_VGA --clkout2 {vga_mhz}")
bsv_out = out.with_suffix(".bsv") bsv_out = out.with_suffix(".bsv")
with open(bsv_out) as f: with open(bsv_out) as f: