Right now, the scope of formal is the following, copied directly from IRC:
(11:56:43 AM) whitequark: the entire scope of formal verification in nmigen at the moment is "it can generate verilog or rtlil with assert/assume statements"
(11:57:10 AM) whitequark: i do not, at the moment, provide anything beyond that, and doing it requires thinking first and coding second
This is a pre-RFC to discuss what other functionality we'd want out of formal integration. Specifically, I'm assuming an end goal will be something along the lines of:
Given a test harness w/ assert/assume and a design w/ assert/assume statements, running a formal verification flow of an Elaboratable in nmigen will be of comparable complexity to synthesizing and programming a bitstream via nmigen.build.plat.Platform.build(). Formal (in symbiyosys, either Bounded Model Check (BMC) and/or k-induction)
To start, I can think of at least three things I want:
-
Minimal hassle to invoke symbiyosys that "does the right thing" on a Fragment or Module (Elaboratable). See above.
-
Per IRC, a formal Platform is probably desirable. Current approach is to inject platform=formal into rtlil/verilog.convert() or similar instead of using nmigen.build. I have some thoughts here:
- I think it's reasonable that
rtlil/verilog.convert() on it's own either emits or excludes asserts/assumes via an option.
- My gut feeling tells that
building for a platform which has assertions and synthesizing a bitstream should be mutually exclusive because synth and formal are meant to do two different tasks. Failing that:
- I would still like the ability to easily clean up formal output artifacts without them intermingling with synthesis output artifacts; for instance, invoking via
sby -d sby_dir formal.sby.
- Input artifact mingling should also be kept to a minimum. A minimum set of inputs affected by emitting for both formal ans synthesis might be: the
sby file, one set of .il/v input for synthesis w/o asserts/assumes, and another set of .il/v for sby.
-
Multiclock support. This one is tougher, it's easy to make a design w/ multiclock fail because the solver will hold one of the clocks steady to prevent forward progress (when a constraint violation is imminent) until n timesteps have elapsed. One way around this is to utilize the $global_clock clock and do something like this for each clock to force each clock to eventually tick:
reg last_clk = 0;
always @($global_clock) begin
last_clk <= clk;
assume(last_clk != clk);
end
When we discussed $global_clock last year, we agreed to not expose $global_clock within nmigen, but we couldn't come up with a good set of constraints that should be autogenerated for formal testbenches with multiple clocks to force forward progress (I think "all n clocks must have ticked at least once in the past n cycles" might work?). I think we should review this.
Right now, the scope of formal is the following, copied directly from IRC:
This is a pre-RFC to discuss what other functionality we'd want out of formal integration. Specifically, I'm assuming an end goal will be something along the lines of:
Given a test harness w/
assert/assumeand a design w/assert/assumestatements, running a formal verification flow of anElaboratableinnmigenwill be of comparable complexity to synthesizing and programming a bitstream vianmigen.build.plat.Platform.build(). Formal (insymbiyosys, either Bounded Model Check (BMC) and/or k-induction)To start, I can think of at least three things I want:
Minimal hassle to invoke
symbiyosysthat "does the right thing" on aFragmentorModule(Elaboratable). See above.Per IRC, a formal
Platformis probably desirable. Current approach is to injectplatform=formalintortlil/verilog.convert()or similar instead of usingnmigen.build. I have some thoughts here:rtlil/verilog.convert()on it's own either emits or excludesasserts/assumes via an option.building for a platform which has assertions and synthesizing a bitstream should be mutually exclusive because synth and formal are meant to do two different tasks. Failing that:sby -d sby_dir formal.sby.sbyfile, one set of.il/vinput for synthesis w/oasserts/assumes, and another set of.il/vforsby.Multiclock support. This one is tougher, it's easy to make a design w/ multiclock fail because the solver will hold one of the clocks steady to prevent forward progress (when a constraint violation is imminent) until
ntimesteps have elapsed. One way around this is to utilize the$global_clockclock and do something like this for each clock to force each clock to eventually tick:When we discussed
$global_clocklast year, we agreed to not expose$global_clockwithinnmigen, but we couldn't come up with a good set of constraints that should be autogenerated for formal testbenches with multiple clocks to force forward progress (I think "allnclocks must have ticked at least once in the pastncycles" might work?). I think we should review this.