Skip to content

Commit

Permalink
Adds ability to specify multiple stabilize tasks on same signal in a …
Browse files Browse the repository at this point in the history
…sv testbench
  • Loading branch information
chaseruskin committed Sep 27, 2024
1 parent 303dabe commit 7b99e6c
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 92 deletions.
156 changes: 82 additions & 74 deletions examples/sv/bcd/bcd_enc_tb.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
#!/usr/bin/env python3

# Project: Verb
# Script: bcd_enc_tb.py
#
# This script generates the I/O test vector files to be used with the
# Generates the I/O test vector files to be used with the
# bcd_enc_tb.vhd testbench. It also produces a coverage report to indicate the
# robust of the tests.

Expand All @@ -12,10 +9,12 @@
from verb.model import *
from verb.coverage import *

# Model the hardware
class BcdEncoder:

def __init__(self, width: int, digits: int):
'''
Create a new hardware model with the defined interface.
'''
self.num_digits = digits
self.width = width

Expand All @@ -31,7 +30,7 @@ def __init__(self, width: int, digits: int):
# 3 | 7 = 3 + 4
# 4 | 9 = 4 + 5
# 5 | 11 = 5 + 6
self.fsm_delay = width+width+1+1
self.fsm_cycle_delay = width+width+1
pass

def eval(self):
Expand Down Expand Up @@ -71,72 +70,81 @@ def eval(self):
pass


# collect parameters
DIGITS = context.generic('DIGITS', type=int)
LEN = context.generic('LEN', type=int)

bcd_algo = BcdEncoder(width=LEN, digits=DIGITS)
bcd_algo_dupe = BcdEncoder(width=LEN, digits=DIGITS)

# Coverage Goals - specify coverage areas

CoverRange("input span") \
.span(bcd_algo.bin.span()) \
.target(bcd_algo.bin) \
.apply()

CoverPoint("overflow enabled") \
.goal(10) \
.bypass(bcd_algo.bin.max() < (10**bcd_algo.num_digits)) \
.target(bcd_algo.ovfl) \
.checker(lambda x: int(x) == 1) \
.apply()

CoverGroup("overflow variants") \
.bins([0, 1]) \
.bypass(bcd_algo.bin.max() < (10**bcd_algo.num_digits)) \
.target(bcd_algo.ovfl) \
.apply()

CoverGroup("extreme inputs") \
.bins([bcd_algo.bin.min(), bcd_algo.bin.max()]) \
.target(bcd_algo.bin) \
.apply()

cp_bin_while_active = CoverPoint("input changes while active") \
.goal(100) \
.apply()

cp_go_while_active = CoverPoint("go while active") \
.goal(100) \
.target(bcd_algo_dupe.go) \
.checker(lambda x: int(x) == 1) \
.apply()


# Generate the test vectors (run the model!)
with vectors('inputs.txt', 'i') as inputs, vectors('outputs.txt', 'o') as outputs:
# initialize the values with defaults
inputs.push(bcd_algo)
while coverage.met(10_000) == False:
# Get a new set of inputs to process
outcome: BcdEncoder = randomize(bcd_algo)
bcd_algo.go.assign(1)
inputs.push(outcome)
# Alter the input while the computation is running
for _ in range(1, outcome.fsm_delay):
outcome_dupe: BcdEncoder = randomize(bcd_algo_dupe)
cp_bin_while_active.check(int(bcd_algo_dupe.bin) != int(bcd_algo.bin))
inputs.push(outcome_dupe)

# Compute the output
bcd_algo.eval()
outputs.push(bcd_algo)

# place some random 'idle' time after a finished computation
for _ in range(0, random.randint(0, 10)):
outcome_dupe: BcdEncoder = randomize(bcd_algo_dupe)
outcome_dupe.go.assign(0)
inputs.push(outcome_dupe)
def main():
# Setup - collect parameters and create models

DIGITS = context.generic('DIGITS', type=int)
LEN = context.generic('LEN', type=int)

bcd_algo = BcdEncoder(width=LEN, digits=DIGITS)
bcd_algo_dupe = BcdEncoder(width=LEN, digits=DIGITS)

# Coverage Goals - specify coverage areas

CoverRange("input span") \
.span(bcd_algo.bin.span()) \
.target(bcd_algo.bin) \
.apply()

CoverPoint("overflow enabled") \
.goal(10) \
.bypass(bcd_algo.bin.max() < (10**bcd_algo.num_digits)) \
.target(bcd_algo.ovfl) \
.checker(lambda x: int(x) == 1) \
.apply()

CoverGroup("overflow variants") \
.bins([0, 1]) \
.bypass(bcd_algo.bin.max() < (10**bcd_algo.num_digits)) \
.target(bcd_algo.ovfl) \
.apply()

CoverGroup("extreme inputs") \
.bins([bcd_algo.bin.min(), bcd_algo.bin.max()]) \
.target(bcd_algo.bin) \
.apply()

cp_bin_while_active = CoverPoint("input changes while active") \
.goal(100) \
.apply()

cp_go_while_active = CoverPoint("go while active") \
.goal(100) \
.target(bcd_algo_dupe.go) \
.checker(lambda x: int(x) == 1) \
.apply()

# Run - generate the test vectors from the model(s)

with vectors('inputs.txt', 'i') as inputs, vectors('outputs.txt', 'o') as outputs:
# initialize the values with defaults
inputs.push(bcd_algo)
while coverage.met(10_000) == False:
# get a new set of inputs to process
outcome: BcdEncoder = randomize(bcd_algo)
bcd_algo.go.assign(1)
inputs.push(outcome)
# alter the input while the computation is running
for _ in range(0, outcome.fsm_cycle_delay):
outcome_dupe: BcdEncoder = randomize(bcd_algo_dupe)
cp_bin_while_active.check(int(bcd_algo_dupe.bin) != int(bcd_algo.bin))
inputs.push(outcome_dupe)

# compute the output
bcd_algo.eval()
outputs.push(bcd_algo)

# place some random 'idle' time after a finished computation
for _ in range(0, random.randint(0, 10)):
outcome_dupe: BcdEncoder = randomize(bcd_algo_dupe)
outcome_dupe.go.assign(0)
inputs.push(outcome_dupe)
pass
pass
pass

pass


if __name__ == '__main__':
main()
pass
5 changes: 3 additions & 2 deletions src/lib/hdl/src/godan.sv
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,6 @@ package godan;
end
endtask;

typedef logic[127:0] logics;

// Checks the logic `data` does not change value when its indicator `flag` is in the active state `active`.
task stabilize(inout int fd, ref logic clk, ref string data, ref logic flag, input logic active, input string subject);
automatic logic is_okay = 1'b1;
Expand Down Expand Up @@ -256,7 +254,10 @@ package godan;
*/

`define stabilize(FD, CLK, DATA, FLAG, ACTIVE, SUBJECT) \
`ifndef \DATA\ \
`define \DATA\ \
string \DATA\ ; \
`endif \
always @(negedge clk) $sformat(\DATA\ , "%b", DATA); \
always stabilize(FD, CLK, \DATA\ , FLAG, ACTIVE, SUBJECT);

Expand Down
36 changes: 20 additions & 16 deletions src/lib/python/verb/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,16 @@ def randomize(model, strategy: str='weights'):
pass
pass
# choose a failing net at random
sel = random.choice(candidates)
values = sel.advance(rand=True)
# force into an iterable type
if type(values) == int:
values = [values]
sources = net.get_source_list()
for i in range(len(sources)):
sources[i].assign(values[i])
if len(candidates) > 0:
sel = random.choice(candidates)
values = sel.advance(rand=True)
# force into an iterable type
if type(values) == int:
values = [values]
sources = sel.get_source_list()
for i in range(len(sources)):
sources[i].assign(values[i])
pass
pass
# select a coverage net according to a weighted distribution using its distance to its goal
elif strat == Strategy.WEIGHTS:
Expand All @@ -132,14 +134,16 @@ def randomize(model, strategy: str='weights'):
for i in weights: total_weight += i
weights = [w/total_weight for w in weights]
# choose a failing net at random
sel = random.choices(candidates, weights=weights)[0]
values = sel.advance(rand=True)
# force into an iterable type
if type(values) == int:
values = [values]
sources = net.get_source_list()
for i in range(len(sources)):
sources[i].assign(values[i])
if len(candidates) > 0:
sel = random.choices(candidates, weights=weights)[0]
values = sel.advance(rand=True)
# force into an iterable type
if type(values) == int:
values = [values]
sources = sel.get_source_list()
for i in range(len(sources)):
sources[i].assign(values[i])
pass
pass

return model
Expand Down

0 comments on commit 7b99e6c

Please sign in to comment.