Skip to content

Commit e0c8204

Browse files
committed
[ci] Add OSS-formal CI
Adds an OSS-Formal CI pass running on ponoma, it takes ~1hr 9mins to run at this time, which is quite fast!
1 parent f65825e commit e0c8204

5 files changed

Lines changed: 79 additions & 7 deletions

File tree

.github/workflows/ci-formal.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright lowRISC contributors.
2+
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# GitHub Actions CI build configuration
6+
7+
name: Ibex OSS Formal CI
8+
9+
on:
10+
push:
11+
paths:
12+
- 'flake.nix'
13+
- 'flake.lock'
14+
- 'dv/formal/**'
15+
- 'rtl/**'
16+
- 'vendor/**'
17+
- '.github/**'
18+
merge_group:
19+
types:
20+
- checks_requested
21+
pull_request:
22+
paths:
23+
- 'flake.nix'
24+
- 'flake.lock'
25+
- 'dv/formal/**'
26+
- 'rtl/**'
27+
- 'vendor/**'
28+
- '.github/**'
29+
30+
jobs:
31+
fv:
32+
name: Run the Open-Source FV flow
33+
runs-on: nixos-25.05
34+
steps:
35+
- uses: actions/checkout@v4
36+
37+
- name: Setup env
38+
run: |
39+
echo "NIX_CONFIG=accept-flake-config = true" >> $GITHUB_ENV
40+
41+
- name: Install Nix
42+
uses: cachix/install-nix-action@v27
43+
with:
44+
extra_nix_config: |
45+
substituters = https://nix-cache.lowrisc.org/public/ https://cache.nixos.org/
46+
trusted-public-keys = nix-cache.lowrisc.org-public-1:O6JLD0yXzaJDPiQW1meVu32JIDViuaPtGDfjlOopU7o= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
47+
48+
- name: Run OSS Env
49+
run: |
50+
source <(nix print-dev-env .#oss-dev)
51+
52+
cd dv/formal
53+
make build/aig-manip
54+
make build/all.aig SHELL=bash
55+
python3 conductor.py prove --check-all
56+

dv/formal/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ build/fusesoc: $(IBEX_ROOT)/rtl $(IBEX_ROOT)/vendor
3333
lowrisc:ibex:ibex_formal:0.1
3434
touch build/fusesoc # Fix timestamps for Makefile
3535

36-
build/ibexspec.sv: $(SAIL) $(SAIL_SRCS) $(SAIL_EXTRA_SRCS) Sources.mk Makefile spec/fix_bugs.py
36+
build/ibexspec.sv: $(SAIL) $(SAIL_SRCS) $(SAIL_EXTRA_SRCS) sail-sources.mk Makefile spec/fix_bugs.py
3737
mkdir -p build
3838
cd build && $(SAIL) $(SAIL_SRCS) $(addprefix ../,$(SAIL_EXTRA_SRCS)) $(SAIL_SV_FLAGS) $(addprefix -sv_unreachable execute_,$(COMPRESSED_INSTRS)) -o ibexspec
3939
python3 spec/fix_bugs.py
@@ -53,6 +53,7 @@ build/yosys_formal.so: yosys_formal/global_clock.cc yosys_formal/write_aiger.cc
5353
yosys-config --build build/yosys_formal.so yosys_formal/global_clock.cc yosys_formal/write_aiger.cc yosys_formal/opt_muxtree.cc
5454

5555
build/aig-manip: aig-manip/src/main.rs
56+
mkdir -p build
5657
cd aig-manip && cargo build --release
5758
cp aig-manip/target/release/aig-manip build/
5859

dv/formal/check/top.sv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright lowRISC contributors.
2-
// Copyright 2024 University of Oxford, see also CREDITS.md.
2+
// Copyright 2025 University of Oxford, see also CREDITS.md.
33
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
44
// Original author: Louis-Emile Ploix
55
// SPDX-License-Identifier: Apache-2.0

dv/formal/conductor.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ def group(props):
448448
parser.add_argument("--hard", action="store_true", help="In explore mode, try harder to prove properties (1hr timeout, more engines).")
449449
parser.add_argument("--no-run", action="store_true", help="In explore mode don't run proofs again to check steps.")
450450
parser.add_argument("--no-kill", action="store_true", help="Don't kill proof processes due to running out of memory.")
451+
parser.add_argument("--check-complete", action="store_true", help="In prove mode, fail if there are unskipped properties without proofs.")
451452
args = parser.parse_args()
452453

453454
SKIPPED_PROPS = [
@@ -535,7 +536,7 @@ async def info_mode(by_step, by_step_skipped):
535536
print(red(f"Step {step} :: UNACCOUNTED :: :: :: {' '.join(unaccounted)}"))
536537
print(f"{total_steps} proof steps in total")
537538

538-
async def prove_mode(by_step):
539+
async def prove_mode(all_props, by_step):
539540
all_strategies = []
540541
for step, props in enumerate(by_step):
541542
if step < args.start:
@@ -555,6 +556,17 @@ async def prove_mode(by_step):
555556
else:
556557
all_strategies.extend(strategy)
557558

559+
if args.check_complete:
560+
covered = set()
561+
for step in all_strategies:
562+
covered.update(step[1])
563+
uncovered = set(prop[1] for prop in all_props).difference(covered)
564+
if len(uncovered) > 0:
565+
print(red(f"Missing proof steps for {len(uncovered)} properties: {' '.join(uncovered)}"))
566+
exit(1)
567+
else:
568+
print(green(f"All {len(all_props)} properties are covered."))
569+
558570
if not args.by_step:
559571
print(white(f"Running strategy for everything"))
560572
run_start = time.time()
@@ -563,12 +575,16 @@ async def prove_mode(by_step):
563575
print(white(f"Ran strategy in {run_dt:.3f}s"))
564576

565577
unsats = 0
578+
has_errors = False
566579
for res, step in results:
567580
if res[0] == 20:
568581
unsats += 1
569582
else:
583+
has_errors = True
570584
print(red(f"Failed to prove step {step[0]} proof step with code {res[0]} (see above, or logfile.txt, for more details): {' '.join(step[1])}"))
571-
print(white(f"{unsats}/{len(all_strategies)} proofs steps were UNSAT"))
585+
print(white(f"{unsats}/{len(all_strategies)} proof steps were UNSAT"))
586+
if has_errors:
587+
exit(1) # Failed
572588

573589
async def construct_strategy(step, props):
574590
strategy = load_strategy(step) or []
@@ -658,7 +674,6 @@ async def logopt(by_step):
658674
continue
659675
[props, step, sha, config] = ins
660676
if len(outs) != 6 or type(outs[0]) != int or type(outs[1]) != float or type(outs[2]) != float or type(outs[3]) != str or type(outs[4]) != str or type(outs[4]) != str:
661-
print("ERR 2")
662677
continue
663678
[code, mem, dt, kind, stdout, stderr] = outs
664679
props.sort()
@@ -740,7 +755,7 @@ def group_by_step(names, max=None):
740755
case "info":
741756
await info_mode(by_step, skipped_by_step)
742757
case "prove":
743-
await prove_mode(by_step)
758+
await prove_mode(props, by_step)
744759
case "explore":
745760
await explore_mode(by_step)
746761
case "logopt":

flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@
206206
oss-dev = mkshell-minimal {
207207
packages = standard_deps ++ [
208208
lowrisc_yosys_slang
209-
(pkgs.yosys.override (attrs: { enablePython = false; }))
209+
((pkgs.yosys.override (attrs: { enablePython = false; })).overrideAttrs (finalAttrs: prev: { doCheck = false; }))
210210
] ++ (with pkgs; [
211211
gtkwave # not stricly necesssary
212212
ric3

0 commit comments

Comments
 (0)