举例(Example)
一、外部断言(External assertions)
这是一个简单计数器和其对应形式化验证的例子:
import spinal.core._
//Here is our DUT
class LimitedCounter extends Component{
//The value register will always be between [2:10]
val value = Reg(UInt(4 bits)) init(2)
when(value < 10){
value := value + 1
}
}
object LimitedCounterFormal extends App {
// import utilities to run the formal verification, but also some utilities to describe formal stuff
import spinal.core.formal._
// Here we run a formal verification which will explore the state space up to 15 cycles to find an assertion failure
FormalConfig.withBMC(15).doVerify(new Component {
// Instanciate our LimitedCounter DUT as a FormalDut, which ensure that all the outputs of the dut are:
// - directly and indirectly driven (no latch / no floating wire)
// - allows the current toplevel to read every signal across the hierarchy
val dut = FormalDut(new LimitedCounter())
// Ensure that the state space start with a proper reset
assumeInitial(ClockDomain.current.isResetActive)
// Check a few things
assert(dut.value >= 2)
assert(dut.value <= 10)
})
}
Internal assertions
If you want you can embed formal statements directly into the DUT:
class LimitedCounterEmbedded extends Component{
val value = Reg(UInt(4 bits)) init(2)
when(value < 10){
value := value + 1
}
// That code block will not be in the SpinalVerilog netlist by default. (would need to enable SpinalConfig().includeFormal. ...
GenerationFlags.formal {
assert(value >= 2)
assert(value <= 10)
}
}
object LimitedCounterEmbeddedFormal extends App {
import spinal.core.formal._
FormalConfig.withBMC(15).doVerify(new Component {
val dut = FormalDut(new LimitedCounterEmbedded())
assumeInitial(ClockDomain.current.isResetActive)
})
}
二、内部断言(Internal assertions)
如果你愿意你也可以把形式化语句直接包含在DUT中:
class LimitedCounterEmbedded extends Component{
val value = Reg(UInt(4 bits)) init(2)
when(value < 10){
value := value + 1
}
// That code block will not be in the SpinalVerilog netlist by default. (would need to enable SpinalConfig().includeFormal. ...
GenerationFlags.formal {
assert(value >= 2)
assert(value <= 10)
}
}
object LimitedCounterEmbeddedFormal extends App {
import spinal.core.formal._
FormalConfig.withBMC(15).doVerify(new Component {
val dut = FormalDut(new LimitedCounterEmbedded())
assumeInitial(ClockDomain.current.isResetActive)
})
}
三、外部激励(External stimulus)
如果你的DUT有输入, 你需要从testbench中驱动他们。所有的一般硬件语句都可以实现它, 但是你也可以用形式化anyseq,qnyconst, allseq, allconst语句:
class LimitedCounterInc extends Component{
//Only increment the value when the inc input is set
val inc = in Bool()
val value = Reg(UInt(4 bits)) init(2)
when(inc && value < 10){
value := value + 1
}
}
object LimitedCounterIncFormal extends App {
import spinal.core.formal._
FormalConfig.withBMC(15).doVerify(new Component {
val dut = FormalDut(new LimitedCounterInc())
assumeInitial(ClockDomain.current.isResetActive)
assert(dut.value >= 2)
assert(dut.value <= 10)
// Drive dut.inc with random values
anyseq(dut.inc)
})
}
四、更多的断言/传递(More assertions/past)
例如我们可以检查值是否计数自增(如果没到10):
FormalConfig.withBMC(15).doVerify(new Component {
val dut = FormalDut(new LimitedCounter())
assumeInitial(ClockDomain.current.isResetActive)
// Check that the value is incrementing.
// hasPast is used to ensure that the past(dut.value) had at least one sampling out of reset
when(pastValid() && past(dut.value) =/= 10){
assert(dut.value === past(dut.value) + 1)
}
})
五、假设存储内容(Assuming memory content)
本例中我们希望防止1
出现在内存中:
class DutWithRam extends Component{
val ram = Mem.fill(4)(UInt(8 bits))
val write = slave(ram.writePort)
val read = slave(ram.readAsyncPort)
}
object FormalRam extends App {
import spinal.core.formal._
FormalConfig.withBMC(15).doVerify(new Component {
val dut = FormalDut(new DutWithRam())
assumeInitial(ClockDomain.current.isResetActive)
// assume that no word in the ram has the value 1
for(i <- 0 until dut.ram.wordCount){
assumeInitial(dut.ram(i) =/= 1)
}
// Allow the write anything but value 1 in the ram
anyseq(dut.write)
clockDomain.withoutReset(){ //As the memory write can occur during reset, we need to ensure the assume apply there too
assume(dut.write.data =/= 1)
}
// Check that no word in the ram is set to 1
anyseq(dut.read.address)
assert(dut.read.data =/= 1)
})
}