Skip to content

Fixture additions #312

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 80 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
3dfc51d
Quick fix adding the minimum amount of code to get the functionality …
standanley Oct 7, 2019
8c92eb0
Fixed style in new test
standanley Oct 8, 2019
e744e1e
Fix indexing into one bit of a spice bus
standanley Oct 16, 2019
e72dda3
Merge pull request #1 from standanley/spice_bus_indexing
standanley Oct 16, 2019
deaea62
Merge remote-tracking branch 'upstream/master'
standanley Nov 12, 2019
311d6d3
Wrote background poke Thread class
standanley Nov 13, 2019
26a0eb4
Finished writing background poke interleaving but have not even run i…
standanley Nov 13, 2019
0368670
I think it is working for spice targets.
standanley Nov 13, 2019
95ddd0b
Working on tests for background pokes
standanley Nov 18, 2019
794d7a3
Changed location of unit conversion in spice_target
standanley Nov 18, 2019
2ff5d92
Added Print action and nothing more
standanley Nov 20, 2019
5276903
Added read to VerilogTarget but no implementation
standanley Nov 20, 2019
7c94e67
Reading now works for systemverilog targets
standanley Nov 21, 2019
78abb7c
Reads work for spice too, based on minimal testing
standanley Nov 21, 2019
64bd264
Edge finder is now working. Need to clean up prints
standanley Nov 22, 2019
6198fa2
Allow indexing into spice buses
standanley Nov 22, 2019
e5f9ecb
Phase measurement now working in spice
standanley Nov 22, 2019
6a61712
Deleted some debugging prints
standanley Nov 22, 2019
4272bb4
Fixed bug in phase measurement and added test
standanley Nov 23, 2019
8a23678
Merged spice_timing into read_action but some of the read_action test…
standanley Nov 23, 2019
35faadf
Changed background poke so it would not break spice reads. Still an i…
standanley Nov 23, 2019
893c859
Fixed default clock step
standanley Dec 3, 2019
7aa57ff
Fixed bug with clocks in background_poke
standanley Dec 5, 2019
bfeba86
Changed pwc to pwl conversion to allow two points within rt of each o…
standanley Dec 5, 2019
2dc8063
Changed spice_target to print stderr of simulation command
standanley Dec 5, 2019
ae39e7a
Marked read action as not implemented in verilator_target
standanley Dec 5, 2019
a6542d0
Bugfix and labeling of error output.
standanley Jan 6, 2020
4939b87
New read styles, decreased default step size in spice transient sim
standanley Jan 22, 2020
096a74f
fixed issue with block reads beyond the end of simulation time
standanley Jan 27, 2020
657e003
Got rid of some references to old style read using save_for_later. St…
standanley Feb 24, 2020
d3800dc
I think I finished the merge but I cannot import fault due to an issu…
standanley Feb 25, 2020
fef2299
Merge branch 'master' into combine_branches
standanley Feb 25, 2020
67db95b
Finished merge and fixed old bug with saves for Read action
standanley Mar 4, 2020
dfa0510
Removed many references to read action.
standanley Mar 4, 2020
1930f7c
Merge branch 'master' into combine_branches
standanley Mar 4, 2020
91e190b
Merge branch 'master' into combine_branches
standanley Mar 4, 2020
f14e6a4
Fixed mistake from earlier merge
standanley Mar 4, 2020
2421029
Changed background poke to a decorator
standanley Mar 4, 2020
764feb2
updated sv target to use new background poke decorator
standanley Mar 4, 2020
cfa3237
Tiny change to avoid mantle comparison that broke on bit type
standanley Mar 17, 2020
30e621b
First pass of understanding mLingua waveforms
standanley Mar 19, 2020
f944500
Working on get_value in different domains. I think it works for syste…
standanley Mar 23, 2020
0f0f90b
Fix domain read for spice. Still some pending todos
standanley Mar 23, 2020
84edb56
Can now read frequency/phase with systemverilog target
standanley Mar 24, 2020
d0e7dad
Fixed a couple bugs in background poke
standanley Mar 25, 2020
eb22b9a
Edge find bug fix
standanley Mar 25, 2020
417361d
Edge find bug fix
standanley Mar 25, 2020
bf34111
Merge branch 'combine_branches' of https://github.com/standanley/faul…
standanley Mar 25, 2020
49ab608
Fix tiny bug with recent changes
standanley Mar 27, 2020
ce9ae41
Merged in master
standanley Apr 2, 2020
a62a3b4
Add flatten method to real_type
standanley Apr 2, 2020
686b158
Merge branch 'add_flatten' into combine_branches
standanley Apr 2, 2020
7c5fe10
Delete traces of old Read action (replaced by GetValue)
standanley Apr 2, 2020
a255b2c
Fix floating point equality comparison
standanley Apr 13, 2020
b921caa
Fixes for arrays of analog and outputs that are arrays
standanley Apr 16, 2020
f990113
Slight update to verilog_target for domain_read
standanley Jun 3, 2020
83b3c73
Working on merge
standanley Jun 15, 2020
41c85ec
Fix mistake in merge of result_parse
standanley Jun 15, 2020
0c1d6d2
Add new type of background poke called future. Doesn't work yet so I'…
standanley Sep 1, 2020
8639507
Updates before dragonphy integration
standanley Oct 3, 2020
773fd1e
Fixed minor merge conflicts in system_verilog_target
standanley Mar 25, 2021
ba86b16
Very minor updates to tests
standanley Mar 26, 2021
30710cd
Very minor organization changes
standanley Mar 26, 2021
a4b0233
Update background_poke and mlingua_interface decorators to handle arb…
standanley Mar 26, 2021
6dfd620
Special case in background_poke for when the port is a Var action. Ri…
standanley Mar 26, 2021
03be393
Working on get_value_domain. Seems to work for SystemVerilog
standanley Mar 30, 2021
63e3bc9
Merge branch 'master' into fixture_additions_merge
standanley Apr 16, 2021
8830a6e
Fix timescale read issue spice result parsing.
standanley Apr 27, 2021
92af1a0
Various style improvements before pull request
standanley Apr 27, 2021
1cb8911
Clean up background poke tests
standanley Apr 27, 2021
6bd9119
Cleanup and bugfixes regarding get value domain for spice circuits
standanley Apr 28, 2021
df7f4b5
Remove mlingua stuff. Hopefully it will be added in the future
standanley Apr 28, 2021
898161e
Style cleanup
standanley Apr 28, 2021
d137e55
More style improvements. Also undid 'style improvement' that broke a …
standanley Apr 28, 2021
10dbb2e
First pass at adding current input in SpiceTarget. Seems to work; cur…
standanley Jun 4, 2021
312f706
Added current outputs
standanley Jun 4, 2021
51b8702
Fix current changes to work with buses. Also fix test_spice_bus to ma…
standanley Jun 8, 2021
00c2469
Minor changes to fix small bugs
standanley Jul 28, 2021
157932c
Rewrite buggy background_poke clock method. I think the new approach …
standanley Aug 12, 2021
4441d2d
Merge pull request #307 from standanley/fixture_additions
leonardt Aug 13, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fault/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .wrapped_internal_port import WrappedVerilogInternalPort
from .ms_types import (RealIn, RealOut, RealType,
CurrentIn, CurrentOut, CurrentType,
ElectIn, ElectOut, ElectType)
from .tester import (Tester, SymbolicTester, PythonTester, TesterBase,
SynchronousTester)
Expand Down
5 changes: 4 additions & 1 deletion fault/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pysv
from fault.select_path import SelectPath
import fault.expression as expression
from fault.domain_read import domain_read


class Action(ABC):
Expand Down Expand Up @@ -96,10 +97,12 @@ def is_output(port):
return not port.is_output()


@domain_read
class GetValue(Action):
def __init__(self, port):
def __init__(self, port, params):
super().__init__()
self.port = port
self.params = params
self.value = None # value to be assigned after simulation

@property
Expand Down
313 changes: 313 additions & 0 deletions fault/background_poke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
from fault.actions import Poke, Var
import heapq
import math
from functools import total_ordering
import copy
from fault.actions import Delay


@total_ordering
class Thread:
# if sin wave dt is unspecified, use period/default_steps_per_cycle
default_steps_per_cycle = 10

# when checking clock value at time t, check time t+epsilon instead
# this avoids ambiguity when landing exactly on the clock edge
# Also, used to check whether a step was supposed to have happened exactly
# on a scheduled update, or is too early and we should reschedule
epsilon = 1e-17

def __init__(self, time, poke):
# print('creating thread for', poke, 'at time', time)
self.poke = copy.copy(poke)
self.poke.params = None
self.poke.delay = None
params = poke.delay
self.start = time
self.next_update = time
type_ = params.get('type', 'clock')

# Each type must set a get_val(t) function and a dt
if type_ == 'clock':
freq = params.get('freq', 1e6)
period = params.get('period', 1/freq)
freq = 1 / period
duty_cycle = params.get('duty_cycle', 0.5)
initial_value = params.get('initial_value', 0)

# had some trouble with epsilon being too small ... I think the
# issue is that the ThreadPool epsilon is in the time domain while
# this one is used in the cycle domain
self.epsilon *= freq

# The way an edge gets dropped is if the time is 0.999 and we decide
# that's close enough to go to 1.5 next, but we also decide to
# output the value for the 0.5-1 range
def get_val(t):
cycle_location = ((t - self.start) / period) % 1
edge_loc = (1-duty_cycle) if initial_value == 0 else duty_cycle
# if we're very close to the edge between regions, assume it
# was an attempt to hit the edge, and opt for later region
first_region = (cycle_location + self.epsilon) % 1 <= edge_loc
cycle_dt = ((edge_loc - cycle_location) % 1 if first_region
else (-cycle_location) % 1)
self.dt = cycle_dt * period
return (not first_region) ^ initial_value

self.get_val = get_val
self.dt = period/2

elif type_ == 'sin':
freq = params.get('freq', 1e6)
period = params.get('period', 1/freq)
freq = 1 / period
amplitude = params.get('amplitude', 1)
offset = params.get('offset', 0)
phase_degrees = params.get('phase_degrees', 0)
conv = math.pi / 180.0
phase_radians = params.get('phase_radians', phase_degrees * conv)

def get_val(t):
x = math.sin((t-self.start)*freq*2*math.pi + phase_radians)
return amplitude * x + offset

self.get_val = get_val
self.dt = params.get('dt', 1 / (freq*self.default_steps_per_cycle))

elif type_ == 'ramp':
start = params.get('start', 0)
stop = params.get('stop', None)
rate = params.get('rate', 1) # volts per second
etol = params.get('etol', 0.1)
assert etol > 0, 'Ramp error tolerance must be positive'
dt = abs(etol / rate)

def get_val(t):
x = start + rate * (t - self.start)
if stop is not None:
if (rate > 0 and x > stop) or (rate < 0 and x < stop):
x = stop
self.dt = float('inf')
return x
self.get_val = get_val
self.dt = dt

elif type_ == 'future':
wait = params.get('wait', None)
waits = params.get('waits', [wait])
value = params.get('value', poke.value)
values = params.get('values', [value])

# for the duration of wait[i], output shoud be value[i]
values = [None] + values
waits = waits + [float('inf')]

self.future_count = 0

def get_val(t):
if t + 2*self.epsilon > self.future_next:
msg = 'Missed update in future background poke'
assert t - self.future_next < self.epsilon, msg
self.future_count += 1
self.future_next += waits[self.future_count]
self.dt = self.future_next - t
v = values[self.future_count]

# Presumably this was here for a reason ... but I don't remember
if abs(t-self.next_update) <= self.epsilon:
if (self.future_next - (self.next_update + self.dt)
< -self.epsilon):
pass
else:
if self.future_next - self.next_update < -self.epsilon:
pass
return v
self.get_val = get_val
self.dt = waits[0]
self.future_next = self.start + self.dt
else:
assert False, 'Unrecognized background_poke type '+str(type_)

def step(self, t):
"""
Returns a new Poke object with the correct value set for time t.
Sets the port and value but NOT the delay.
"""
# TODO don't poke at the same time twice
missed_update_msg = 'Background Poke thread not updated in time'
assert t <= self.next_update + self.epsilon, missed_update_msg

# must call get_val before calculating next_update because it can
# alter self.dt
value = self.get_val(t)
if abs(t - self.next_update) < 2*self.epsilon:
self.next_update = t + self.dt

if value is None:
# The actual delay will get set later
return Delay(-1)
else:
poke = copy.copy(self.poke)
poke.value = value
return poke

def __lt__(self, other):
return self.next_update < other


class ThreadPool:
# if the next background update is within epsilon of the next manual
# update, then the background one comes first
# Which comes first is arbitrary, but this epsilon makes it consistent
epsilon = 1e-17

def __init__(self, time):
self.t = time
self.background_threads = []
self.active_ports = set()

@staticmethod
def set_action_delay(action, delay):
if isinstance(action, Delay):
action.time = delay
else:
action.delay = delay

def get_next_update_time(self):
if len(self.background_threads) == 0:
return float('inf')
else:
return self.background_threads[0].next_update

def delay(self, t_delay):
"""
Create a list of actions that need to happen during this delay.
Returns (free_time, actions), where free_time is the delay that should
happen before the first action in actions.
"""
t = self.t
t_end = self.t + t_delay
# note that free_time here is actually the free time until the next
# pool update, the actual time returned is until the next action and
# might be less than free_time
free_time = self.get_next_update_time() - self.t
if free_time > t_delay:
self.t = t_end
return t_delay, []
else:
actions = []
t += free_time
while self.get_next_update_time() <= t_end + self.epsilon:
thread = heapq.heappop(self.background_threads)
action = thread.step(t)
heapq.heappush(self.background_threads, thread)

# we had to put the thread back on the heap in order to
# calculate the next update
next_thing_time = min(self.get_next_update_time(), t_end)
delay = next_thing_time - t
self.set_action_delay(action, delay)
t = next_thing_time
actions.append(action)

# t_end has less floating point error than t
self.t = t_end
return free_time, actions

def add(self, background_poke):
error_msg = 'Cannot add existing background thread'
assert background_poke.port not in self.active_ports, error_msg
self.active_ports.add(background_poke.port)
thread = Thread(self.t, background_poke)
heapq.heappush(self.background_threads, thread)

def remove(self, port):
self.active_ports.remove(port)
for thread in self.background_threads:
if thread.poke.port is port:
offender = thread
break
else:
msg = 'background_poke internal error: Could not find remove port'
assert False, msg
self.background_threads.remove(offender)
heapq.heapify(self.background_threads)
poke = offender.step(self.t)
if poke is None:
# TODO I don't think this path is ever taken?
return []
else:
self.set_action_delay(poke, 0)
return [poke]

def process(self, action, delay):
new_action_list = []
is_background = (isinstance(action, Poke)
and type(action.delay) == dict)

# check whether this is a poke taking over a background port
# TODO if the port is a Var, just hope it's not taking over
if (isinstance(action, Poke)
and not isinstance(action.port, Var)
and action.port in self.active_ports):
new_action_list += self.remove(action.port)

# if the new port is background we must add it before doing delay
if is_background:
self.add(action)

# we might cut action's delay short to allow some background pokes
new_delay, actions = self.delay(delay)

# now we add this (shortened) action back in
if not is_background:
# NOTE: we used to use copies of the action so we weren't editing
# the delay of an action owned by someone else. But with the new
# GetValue action it's important that the object doesn't change
# because the user is holding a pointer to the old GetValue object
self.set_action_delay(action, new_delay)
new_action_list.append(action)

new_action_list += actions
return new_action_list


def process_action_list(actions, clock_step_delay):
"""
Replace Pokes with background_params with many individual pokes.
Automatically interleaves multiple background tasks with other pokes.
Throws a NotImplementedError if there's a background task during an
interval of time not known at compile time.
"""

def get_delay(a):
if not hasattr(a, 'delay'):
return getattr(a, 'time', 0)
elif a.delay is not None:
if type(a.delay) == dict:
return 0
else:
return a.delay
else:
# TODO this case used to use clock_step_delay instead of 0
# I'm not sure exactly when it's appropriate to do that
return 0 # clock_step_delay

background_pool = ThreadPool(0)
new_action_list = []
for action in actions:
delay = get_delay(action)
new_action_list += background_pool.process(action, delay)
return new_action_list


def background_poke_target(cls):
class BackgroundPokeTarget(cls):
def run(self, *args, **kwargs):
assert (len(args) > 0 and isinstance(args[0], list),
'Expected first arg to "target.run" to be an action list')
actions = args[0]
new_actions = process_action_list(actions, self.clock_step_delay)
new_args = (new_actions, *args[1:])
super().run(*new_args, **kwargs)
return BackgroundPokeTarget
Loading