Skip to content

Commit 1f2232a

Browse files
mndstrmrmarnovandermaas
authored andcommitted
[ci] Have an OSS-Formal CI Pass
This commit introduces a new ci job which invokes the OS formal flow. It uses the nix flake, which contains all the necessary dependencies. With everything already in cache this takes less than an hour on pomona. A parameter (MAX_MEM_GB) can be used to limit memory usage.
1 parent 92d4c7f commit 1f2232a

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

.github/workflows/ci-formal.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
pull_request:
11+
branches:
12+
- '*'
13+
14+
jobs:
15+
fv:
16+
name: Run the Open-Source FV flow
17+
runs-on: nixos
18+
concurrency:
19+
group: oss-fv
20+
cancel-in-progress: false # Only do one oss-fv job at a time, since we will use a huge amount of memory
21+
steps:
22+
- name: Setup env
23+
run: |
24+
echo "MAX_MEM_GB=100" >> $GITHUB_ENV # Maximum memory available to conductor.py, which is inversely proportional to time. 200GB -> 40 mins
25+
echo "NIX_CONFIG=accept-flake-config = true" >> $GITHUB_ENV
26+
27+
- name: Notify about queued execution
28+
run: |
29+
echo "Warning: This job will block other oss-fv jobs until it finishes, since it uses a lot of memory."
30+
31+
- uses: actions/checkout@v4
32+
33+
- name: Install Nix
34+
uses: cachix/install-nix-action@v27
35+
with:
36+
extra_nix_config: |
37+
substituters = https://nix-cache.lowrisc.org/public/ https://cache.nixos.org/
38+
trusted-public-keys = nix-cache.lowrisc.org-public-1:O6JLD0yXzaJDPiQW1meVu32JIDViuaPtGDfjlOopU7o= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
39+
40+
- name: Run OSS Env
41+
run: |
42+
source <(nix print-dev-env .#oss-dev)
43+
44+
cd dv/formal
45+
make build/aig-manip
46+
make build/all.aig SHELL=bash
47+
python3 conductor.py prove --check-complete --max-mem $MAX_MEM_GB
48+

dv/formal/conductor.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,13 @@ class ProcessRunner:
225225
MAX_RUNNING = (psutil.cpu_count() or 8) - 2 # Number of CPUs, with a couple spares
226226

227227
def __init__(self):
228-
log(f"Process runner will have a maximum of {ProcessRunner.MAX_RUNNING} processes, and currently sees {global_memory_free():.3f}GB free.")
229-
230228
self.pending = []
231229
self.running = []
232230
self.first_start = time.time()
233231
self.debug_count = 0
234232

233+
log(f"Process runner will have a maximum of {ProcessRunner.MAX_RUNNING} processes, and currently sees {self.mem_avail():.3f}GB free.")
234+
235235
def append(self, proc):
236236
self.pending.insert(0, proc)
237237

@@ -244,7 +244,7 @@ def children_used_mem(self):
244244
def mem_avail(self):
245245
if args.max_mem == 0:
246246
return global_memory_free()
247-
return max(args.max_mem - self.children_used_mem(), 0)
247+
return min(global_memory_free(), max(args.max_mem - self.children_used_mem(), 0))
248248

249249
def poll(self):
250250
# Kill recently started processes until memory is OK, unless there is just one, then there's no point(?)
@@ -279,9 +279,10 @@ def poll(self):
279279

280280
asyncio.get_running_loop().call_later(ProcessRunner.POLL_DELAY, lambda: self.poll())
281281

282-
process_runner = ProcessRunner()
282+
process_runner: ProcessRunner | None = None
283283
'''Run a shell command in the global process runner'''
284284
async def shell(cmd, expected_memory = 0.0, timeout = None, debug_slow = None):
285+
assert process_runner is not None
285286
proc = Process(cmd, expected_memory=expected_memory, timeout=timeout, debug_slow=debug_slow)
286287
process_runner.append(proc)
287288
return await proc.future
@@ -720,6 +721,8 @@ async def explore_mode(by_step: list[list[str]]):
720721
log(f"Skipping proof run for step {step}, since it has just one step", c=white)
721722

722723
async def main():
724+
global process_runner
725+
723726
def preproc_name(name: str) -> tuple[int, str]:
724727
first = name.split("$")[1][5:]
725728
assert first.startswith("Step")
@@ -738,6 +741,7 @@ def group_by_step(names: list[tuple[int, str]], max=None) -> list[list[str]]:
738741
by_step.append([])
739742
return by_step
740743

744+
process_runner = ProcessRunner()
741745
process_runner.start_loop()
742746

743747
log("Reading property list", c=white)

0 commit comments

Comments
 (0)