transforms: quantize time before interleaving

This commit is contained in:
Sebastien Bourdeauducq 2014-11-15 15:26:35 -07:00
parent ddc9c3423f
commit e02ca0b404
3 changed files with 108 additions and 63 deletions

View File

@ -2,6 +2,7 @@ import os
from artiq.transforms.inline import inline from artiq.transforms.inline import inline
from artiq.transforms.lower_units import lower_units from artiq.transforms.lower_units import lower_units
from artiq.transforms.quantize_time import quantize_time
from artiq.transforms.remove_inter_assigns import remove_inter_assigns from artiq.transforms.remove_inter_assigns import remove_inter_assigns
from artiq.transforms.fold_constants import fold_constants from artiq.transforms.fold_constants import fold_constants
from artiq.transforms.remove_dead_code import remove_dead_code from artiq.transforms.remove_dead_code import remove_dead_code
@ -54,6 +55,9 @@ class Core:
remove_inter_assigns(func_def) remove_inter_assigns(func_def)
debug_unparse("remove_inter_assigns_1", func_def) debug_unparse("remove_inter_assigns_1", func_def)
quantize_time(func_def, self.runtime_env.ref_period)
debug_unparse("quantize_time", func_def)
fold_constants(func_def) fold_constants(func_def)
debug_unparse("fold_constants_1", func_def) debug_unparse("fold_constants_1", func_def)
@ -63,9 +67,7 @@ class Core:
interleave(func_def) interleave(func_def)
debug_unparse("interleave", func_def) debug_unparse("interleave", func_def)
lower_time(func_def, lower_time(func_def, getattr(self.runtime_env, "initial_time", 0))
getattr(self.runtime_env, "initial_time", 0),
self.runtime_env.ref_period)
debug_unparse("lower_time", func_def) debug_unparse("lower_time", func_def)
remove_inter_assigns(func_def) remove_inter_assigns(func_def)

View File

@ -1,64 +1,29 @@
"""This transform implements time management functions (delay/now/at)
using an accumulator 'now' and simple replacement rules:
delay(t) -> now += t
now() -> now
at(t) -> now = t
Time parameters must be quantized to integers before running this transform.
The accumulator is initialized to an int64 value at the beginning of the
output function.
"""
import ast import ast
from artiq.transforms.tools import value_to_ast from artiq.transforms.tools import value_to_ast
from artiq.language.core import int64 from artiq.language.core import int64
def _time_to_cycles(ref_period, node):
divided = ast.copy_location(
ast.BinOp(left=node,
op=ast.Div(),
right=value_to_ast(ref_period)),
node)
return ast.copy_location(
ast.Call(func=ast.Name("round64", ast.Load()),
args=[divided],
keywords=[], starargs=[], kwargs=[]),
divided)
def _time_to_cycles_opt(ref_period, node):
if (isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == "cycles_to_time"):
return node.args[0]
else:
return _time_to_cycles(ref_period, node)
def _cycles_to_time(ref_period, node):
return ast.copy_location(
ast.BinOp(left=node,
op=ast.Mult(),
right=value_to_ast(ref_period)),
node)
class _TimeLowerer(ast.NodeTransformer): class _TimeLowerer(ast.NodeTransformer):
def __init__(self, ref_period):
self.ref_period = ref_period
def visit_Call(self, node): def visit_Call(self, node):
# optimize time_to_cycles(now()) -> now if isinstance(node.func, ast.Name) and node.func.id == "now":
if (isinstance(node.func, ast.Name)
and node.func.id == "time_to_cycles"
and isinstance(node.args[0], ast.Call)
and isinstance(node.args[0].func, ast.Name)
and node.args[0].func.id == "now"):
return ast.copy_location(ast.Name("now", ast.Load()), node) return ast.copy_location(ast.Name("now", ast.Load()), node)
else:
self.generic_visit(node) self.generic_visit(node)
if isinstance(node.func, ast.Name): return node
funcname = node.func.id
if funcname == "now":
return _cycles_to_time(
self.ref_period,
ast.copy_location(ast.Name("now", ast.Load()), node))
elif funcname == "time_to_cycles":
return _time_to_cycles(self.ref_period, node.args[0])
elif funcname == "cycles_to_time":
return _cycles_to_time(self.ref_period, node.args[0])
return node
def visit_Expr(self, node): def visit_Expr(self, node):
r = node r = node
@ -69,23 +34,19 @@ class _TimeLowerer(ast.NodeTransformer):
r = ast.copy_location( r = ast.copy_location(
ast.AugAssign(target=ast.Name("now", ast.Store()), ast.AugAssign(target=ast.Name("now", ast.Store()),
op=ast.Add(), op=ast.Add(),
value=_time_to_cycles_opt( value=node.value.args[0]),
self.ref_period,
node.value.args[0])),
node) node)
elif funcname == "at": elif funcname == "at":
r = ast.copy_location( r = ast.copy_location(
ast.Assign(targets=[ast.Name("now", ast.Store())], ast.Assign(targets=[ast.Name("now", ast.Store())],
value=_time_to_cycles_opt( value=node.value.args[0]),
self.ref_period,
node.value.args[0])),
node) node)
self.generic_visit(r) self.generic_visit(r)
return r return r
def lower_time(func_def, initial_time, ref_period): def lower_time(func_def, initial_time):
_TimeLowerer(ref_period).visit(func_def) _TimeLowerer().visit(func_def)
func_def.body.insert(0, ast.copy_location( func_def.body.insert(0, ast.copy_location(
ast.Assign(targets=[ast.Name("now", ast.Store())], ast.Assign(targets=[ast.Name("now", ast.Store())],
value=value_to_ast(int64(initial_time))), value=value_to_ast(int64(initial_time))),

View File

@ -0,0 +1,82 @@
"""This transform turns calls to delay/now/at that use non-integer time
expressed in seconds into calls that use int64 time expressed in multiples of
ref_period.
It does so by inserting multiplication/division/rounding operations around
those calls.
The time_to_cycles and cycles_to_time core language functions are also
implemented here.
"""
import ast
from artiq.transforms.tools import value_to_ast
def _call_now(node):
return ast.copy_location(
ast.Call(func=ast.Name("now", ast.Load()),
args=[], keywords=[], starargs=[], kwargs=[]),
node)
def _time_to_cycles(ref_period, node):
divided = ast.copy_location(
ast.BinOp(left=node,
op=ast.Div(),
right=value_to_ast(ref_period)),
node)
return ast.copy_location(
ast.Call(func=ast.Name("round64", ast.Load()),
args=[divided],
keywords=[], starargs=[], kwargs=[]),
divided)
def _cycles_to_time(ref_period, node):
return ast.copy_location(
ast.BinOp(left=node,
op=ast.Mult(),
right=value_to_ast(ref_period)),
node)
class _TimeQuantizer(ast.NodeTransformer):
def __init__(self, ref_period):
self.ref_period = ref_period
def visit_Call(self, node):
funcname = node.func.id
if funcname == "now":
return _cycles_to_time(self.ref_period, _call_now(node))
elif funcname == "delay" or funcname == "at":
if (isinstance(node.args[0], ast.Call)
and node.args[0].func.id == "cycles_to_time"):
# optimize:
# delay/at(cycles_to_time(x)) -> delay/at(x)
node.args[0] = self.visit(node.args[0].args[0])
else:
node.args[0] = _time_to_cycles(self.ref_period,
self.visit(node.args[0]))
return node
elif funcname == "time_to_cycles":
if (isinstance(node.args[0], ast.Call)
and node.args[0].func.id == "now"):
# optimize:
# time_to_cycles(now()) -> now()
return _call_now(node)
else:
return _time_to_cycles(self.ref_period,
self.visit(node.args[0]))
elif funcname == "cycles_to_time":
return _cycles_to_time(self.ref_period,
self.visit(node.args[0]))
else:
self.generic_visit(node)
return node
def quantize_time(func_def, ref_period):
_TimeQuantizer(ref_period).visit(func_def)