mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
move pytholite to separate repos
This commit is contained in:
parent
c824379878
commit
beeaefccea
10 changed files with 0 additions and 1105 deletions
|
@ -1,46 +0,0 @@
|
|||
from migen.flow.network import *
|
||||
from migen.flow.transactions import *
|
||||
from migen.actorlib.sim import *
|
||||
from migen.pytholite.compiler import Pytholite
|
||||
from migen.sim.generic import run_simulation
|
||||
from migen.fhdl import verilog
|
||||
|
||||
layout = [("r", 32)]
|
||||
|
||||
def number_gen(n):
|
||||
for i in range(n):
|
||||
yield Token("result", {"r": i})
|
||||
|
||||
class SimNumberGen(SimActor):
|
||||
def __init__(self):
|
||||
self.result = Source(layout)
|
||||
SimActor.__init__(self, number_gen(5))
|
||||
|
||||
def run_ng_sim(ng):
|
||||
g = DataFlowGraph()
|
||||
d = Dumper(layout)
|
||||
g.add_connection(ng, d)
|
||||
|
||||
c = CompositeActor(g)
|
||||
run_simulation(c, ncycles=20)
|
||||
|
||||
def make_ng_pytholite():
|
||||
ng_pytholite = Pytholite(number_gen, 5)
|
||||
ng_pytholite.result = Source(layout)
|
||||
ng_pytholite.finalize()
|
||||
return ng_pytholite
|
||||
|
||||
def main():
|
||||
print("Simulating native Python:")
|
||||
ng_native = SimNumberGen()
|
||||
run_ng_sim(ng_native)
|
||||
|
||||
print("Simulating Pytholite:")
|
||||
ng_pytholite = make_ng_pytholite()
|
||||
run_ng_sim(ng_pytholite)
|
||||
|
||||
print("Converting Pytholite to Verilog:")
|
||||
ng_pytholite = make_ng_pytholite()
|
||||
print(verilog.convert(ng_pytholite))
|
||||
|
||||
main()
|
|
@ -1,67 +0,0 @@
|
|||
from migen.flow.network import *
|
||||
from migen.flow.transactions import *
|
||||
from migen.actorlib.sim import Dumper
|
||||
from migen.bus import wishbone
|
||||
from migen.bus.transactions import *
|
||||
from migen.genlib.ioo import UnifiedIOSimulation
|
||||
from migen.pytholite.transel import Register
|
||||
from migen.pytholite.compiler import Pytholite
|
||||
from migen.sim.generic import run_simulation
|
||||
from migen.fhdl.std import *
|
||||
from migen.fhdl import verilog
|
||||
|
||||
layout = [("r", 32)]
|
||||
|
||||
def gen():
|
||||
ds = Register(32)
|
||||
for i in range(3):
|
||||
r = TRead(i, busname="mem")
|
||||
yield r
|
||||
ds.store = r.data
|
||||
yield Token("result", {"r": ds})
|
||||
for i in range(5):
|
||||
r = TRead(i, busname="wb")
|
||||
yield r
|
||||
ds.store = r.data
|
||||
yield Token("result", {"r": ds})
|
||||
|
||||
class SlaveModel(wishbone.TargetModel):
|
||||
def read(self, address):
|
||||
return address + 4
|
||||
|
||||
class TestBench(Module):
|
||||
def __init__(self, ng):
|
||||
g = DataFlowGraph()
|
||||
d = Dumper(layout)
|
||||
g.add_connection(ng, d)
|
||||
|
||||
self.submodules.slave = wishbone.Target(SlaveModel())
|
||||
self.submodules.intercon = wishbone.InterconnectPointToPoint(ng.wb, self.slave.bus)
|
||||
self.submodules.ca = CompositeActor(g)
|
||||
|
||||
def run_ng_sim(ng):
|
||||
run_simulation(TestBench(ng), ncycles=50)
|
||||
|
||||
def add_interfaces(obj):
|
||||
obj.result = Source(layout)
|
||||
obj.wb = wishbone.Interface()
|
||||
obj.mem = Memory(32, 3, init=[42, 37, 81])
|
||||
obj.finalize()
|
||||
|
||||
def main():
|
||||
print("Simulating native Python:")
|
||||
ng_native = UnifiedIOSimulation(gen())
|
||||
add_interfaces(ng_native)
|
||||
run_ng_sim(ng_native)
|
||||
|
||||
print("Simulating Pytholite:")
|
||||
ng_pytholite = Pytholite(gen)
|
||||
add_interfaces(ng_pytholite)
|
||||
run_ng_sim(ng_pytholite)
|
||||
|
||||
print("Converting Pytholite to Verilog:")
|
||||
ng_pytholite = Pytholite(gen)
|
||||
add_interfaces(ng_pytholite)
|
||||
print(verilog.convert(ng_pytholite))
|
||||
|
||||
main()
|
|
@ -1,79 +0,0 @@
|
|||
from migen.util.misc import xdir
|
||||
from migen.fhdl.std import *
|
||||
from migen.flow.actor import *
|
||||
from migen.flow.actor import _Endpoint
|
||||
from migen.flow.transactions import *
|
||||
from migen.actorlib.sim import TokenExchanger
|
||||
from migen.bus import wishbone, memory
|
||||
from migen.bus.transactions import *
|
||||
|
||||
class UnifiedIOObject(Module):
|
||||
def do_finalize(self):
|
||||
if self.get_dataflow():
|
||||
self.busy = Signal()
|
||||
self.specials += set(v for k, v in xdir(self, True) if isinstance(v, Memory))
|
||||
|
||||
def get_dataflow(self):
|
||||
return dict((k, v) for k, v in xdir(self, True) if isinstance(v, _Endpoint))
|
||||
|
||||
def get_buses(self):
|
||||
return dict((k, v) for k, v in xdir(self, True) if isinstance(v, (wishbone.Interface, Memory)))
|
||||
|
||||
(_WAIT_COMPLETE, _WAIT_POLL) = range(2)
|
||||
|
||||
class UnifiedIOSimulation(UnifiedIOObject):
|
||||
def __init__(self, generator):
|
||||
self.generator = generator
|
||||
|
||||
def do_finalize(self):
|
||||
UnifiedIOObject.do_finalize(self)
|
||||
callers = []
|
||||
self.busname_to_caller_id = {}
|
||||
if self.get_dataflow():
|
||||
callers.append(TokenExchanger(self.dispatch_g(0), self))
|
||||
for k, v in self.get_buses().items():
|
||||
caller_id = len(callers)
|
||||
self.busname_to_caller_id[k] = caller_id
|
||||
g = self.dispatch_g(caller_id)
|
||||
if isinstance(v, wishbone.Interface):
|
||||
caller = wishbone.Initiator(g, v)
|
||||
elif isinstance(v, Memory):
|
||||
caller = memory.Initiator(g, v)
|
||||
callers.append(caller)
|
||||
self.submodules += callers
|
||||
|
||||
self.dispatch_state = _WAIT_COMPLETE
|
||||
self.dispatch_caller = 0
|
||||
self.pending_transaction = None
|
||||
|
||||
def identify_transaction(self, t):
|
||||
if isinstance(t, Token):
|
||||
return 0
|
||||
elif isinstance(t, TRead) or isinstance(t, TWrite):
|
||||
if t.busname is None:
|
||||
if len(self.busname_to_caller_id) != 1:
|
||||
raise TypeError
|
||||
else:
|
||||
return list(self.busname_to_caller_id.values())[0]
|
||||
else:
|
||||
return self.busname_to_caller_id[t.busname]
|
||||
else:
|
||||
raise TypeError
|
||||
|
||||
def dispatch_g(self, caller_id):
|
||||
while True:
|
||||
if self.dispatch_state == _WAIT_COMPLETE and self.dispatch_caller == caller_id:
|
||||
transaction = next(self.generator)
|
||||
tr_cid = self.identify_transaction(transaction)
|
||||
self.dispatch_caller = tr_cid
|
||||
if tr_cid == caller_id:
|
||||
yield transaction
|
||||
else:
|
||||
self.pending_transaction = transaction
|
||||
self.dispatch_state = _WAIT_POLL
|
||||
yield None
|
||||
elif self.dispatch_state == _WAIT_POLL and self.dispatch_caller == caller_id:
|
||||
self.dispatch_state = _WAIT_COMPLETE
|
||||
yield self.pending_transaction
|
||||
else:
|
||||
yield None
|
|
@ -1,318 +0,0 @@
|
|||
import inspect
|
||||
import ast
|
||||
from collections import OrderedDict
|
||||
|
||||
from migen.util.misc import xdir
|
||||
from migen.fhdl.structure import *
|
||||
from migen.fhdl.visit import TransformModule
|
||||
from migen.fhdl.specials import Memory
|
||||
from migen.genlib.ioo import UnifiedIOObject
|
||||
from migen.pytholite.reg import *
|
||||
from migen.pytholite.expr import *
|
||||
from migen.pytholite import transel
|
||||
from migen.pytholite.io import gen_io
|
||||
from migen.pytholite.util import *
|
||||
|
||||
def _is_name_used(node, name):
|
||||
for n in ast.walk(node):
|
||||
if isinstance(n, ast.Name) and n.id == name:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _make_function_args_dict(undefined, symdict, args, defaults):
|
||||
d = OrderedDict()
|
||||
for argument in args:
|
||||
d[argument.arg] = undefined
|
||||
for default, argname in zip(defaults, reversed(list(d.keys()))):
|
||||
default_val = eval_ast(default, symdict)
|
||||
d[argname] = default_val
|
||||
return d
|
||||
|
||||
def _process_function_args(symdict, function_def, args, kwargs):
|
||||
defargs = function_def.args
|
||||
undefined = object()
|
||||
|
||||
ad_positional = _make_function_args_dict(undefined, symdict, defargs.args, defargs.defaults)
|
||||
vararg_name = defargs.vararg
|
||||
kwarg_name = defargs.kwarg
|
||||
ad_kwonly = _make_function_args_dict(undefined, symdict, defargs.kwonlyargs, defargs.kw_defaults)
|
||||
|
||||
# grab argument values
|
||||
current_argvalue = iter(args)
|
||||
try:
|
||||
for argname in ad_positional.keys():
|
||||
ad_positional[argname] = next(current_argvalue)
|
||||
except StopIteration:
|
||||
pass
|
||||
vararg = tuple(current_argvalue)
|
||||
|
||||
kwarg = dict()
|
||||
for k, v in kwargs.items():
|
||||
if k in ad_positional:
|
||||
ad_positional[k] = v
|
||||
elif k in ad_kwonly:
|
||||
ad_kwonly[k] = v
|
||||
else:
|
||||
kwarg[k] = v
|
||||
|
||||
# check
|
||||
undefined_pos = [k for k, v in ad_positional.items() if v is undefined]
|
||||
if undefined_pos:
|
||||
formatted = " and ".join("'" + k + "'" for k in undefined_pos)
|
||||
raise TypeError("Missing required positional arguments: " + formatted)
|
||||
if vararg and vararg_name is None:
|
||||
raise TypeError("Function takes {} positional arguments but {} were given".format(len(ad_positional),
|
||||
len(ad_positional) + len(vararg)))
|
||||
ad_kwonly = [k for k, v in ad_positional.items() if v is undefined]
|
||||
if undefined_pos:
|
||||
formatted = " and ".join("'" + k + "'" for k in undefined_pos)
|
||||
raise TypeError("Missing required keyword-only arguments: " + formatted)
|
||||
if kwarg and kwarg_name is None:
|
||||
formatted = " and ".join("'" + k + "'" for k in kwarg.keys())
|
||||
raise TypeError("Got unexpected keyword arguments: " + formatted)
|
||||
|
||||
# update symdict
|
||||
symdict.update(ad_positional)
|
||||
if vararg_name is not None:
|
||||
symdict[vararg_name] = vararg
|
||||
symdict.update(ad_kwonly)
|
||||
if kwarg_name is not None:
|
||||
symdict[kwarg_name] = kwarg
|
||||
|
||||
class _Compiler:
|
||||
def __init__(self, ioo, symdict, registers):
|
||||
self.ioo = ioo
|
||||
self.symdict = symdict
|
||||
self.registers = registers
|
||||
self.ec = ExprCompiler(self.symdict)
|
||||
|
||||
def visit_top(self, node, args, kwargs):
|
||||
if isinstance(node, ast.Module) \
|
||||
and len(node.body) == 1 \
|
||||
and isinstance(node.body[0], ast.FunctionDef):
|
||||
function_def = node.body[0]
|
||||
_process_function_args(self.symdict, function_def, args, kwargs)
|
||||
states, exit_states = self.visit_block(function_def.body)
|
||||
return states
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
# blocks and statements
|
||||
def visit_block(self, statements):
|
||||
sa = StateAssembler()
|
||||
statements = iter(statements)
|
||||
statement = None
|
||||
while True:
|
||||
if statement is None:
|
||||
try:
|
||||
statement = next(statements)
|
||||
except StopIteration:
|
||||
return sa.ret()
|
||||
if isinstance(statement, ast.Assign):
|
||||
# visit_assign can recognize a I/O pattern, consume several
|
||||
# statements from the iterator and return the first statement
|
||||
# that is not part of the I/O pattern anymore.
|
||||
statement = self.visit_assign(sa, statement, statements)
|
||||
else:
|
||||
if isinstance(statement, ast.If):
|
||||
self.visit_if(sa, statement)
|
||||
elif isinstance(statement, ast.While):
|
||||
self.visit_while(sa, statement)
|
||||
elif isinstance(statement, ast.For):
|
||||
self.visit_for(sa, statement)
|
||||
elif isinstance(statement, ast.Expr):
|
||||
self.visit_expr_statement(sa, statement)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
statement = None
|
||||
|
||||
def visit_assign(self, sa, node, statements):
|
||||
if isinstance(node.value, ast.Call):
|
||||
is_special = False
|
||||
try:
|
||||
value = self.ec.visit_expr_call(node.value)
|
||||
except NotImplementedError:
|
||||
is_special = True
|
||||
if is_special:
|
||||
return self.visit_assign_special(sa, node, statements)
|
||||
else:
|
||||
value = self.ec.visit_expr(node.value)
|
||||
if isinstance(value, (int, bool, Value)):
|
||||
r = []
|
||||
for target in node.targets:
|
||||
if isinstance(target, ast.Attribute) and target.attr == "store":
|
||||
treg = target.value
|
||||
if isinstance(treg, ast.Name):
|
||||
r.append(self.symdict[treg.id].load(value))
|
||||
else:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
raise NotImplementedError
|
||||
sa.assemble([r], [r])
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def visit_assign_special(self, sa, node, statements):
|
||||
value = node.value
|
||||
assert(isinstance(value, ast.Call))
|
||||
if isinstance(value.func, ast.Name):
|
||||
callee = self.symdict[value.func.id]
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
if callee == transel.Register:
|
||||
if len(value.args) != 1:
|
||||
raise TypeError("Register() takes exactly 1 argument")
|
||||
bits_sign = eval_ast(value.args[0], self.symdict)
|
||||
if isinstance(node.targets[0], ast.Name):
|
||||
targetname = node.targets[0].id
|
||||
else:
|
||||
targetname = "unk"
|
||||
reg = ImplRegister(targetname, bits_sign)
|
||||
self.registers.append(reg)
|
||||
for target in node.targets:
|
||||
if isinstance(target, ast.Name):
|
||||
self.symdict[target.id] = reg
|
||||
else:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
return self.visit_io_pattern(sa, node.targets, callee, value.args, value.keywords, statements)
|
||||
|
||||
def visit_io_pattern(self, sa, targets, model, args, keywords, statements):
|
||||
# first statement is <modelname> = <model>(<args>)
|
||||
if len(targets) != 1 or not isinstance(targets[0], ast.Name):
|
||||
raise NotImplementedError("Unrecognized I/O pattern")
|
||||
modelname = targets[0].id
|
||||
if modelname in self.symdict:
|
||||
raise NotImplementedError("I/O model name is not free")
|
||||
|
||||
# second statement must be yield <modelname>
|
||||
try:
|
||||
ystatement = next(statements)
|
||||
except StopIteration:
|
||||
raise NotImplementedError("Incomplete or fragmented I/O pattern")
|
||||
if not isinstance(ystatement, ast.Expr) \
|
||||
or not isinstance(ystatement.value, ast.Yield) \
|
||||
or not isinstance(ystatement.value.value, ast.Name) \
|
||||
or ystatement.value.value.id != modelname:
|
||||
print(ast.dump(ystatement))
|
||||
raise NotImplementedError("Unrecognized I/O pattern")
|
||||
|
||||
# following optional statements are assignments to registers
|
||||
# with <modelname> used in expressions.
|
||||
from_model = []
|
||||
while True:
|
||||
try:
|
||||
fstatement = next(statements)
|
||||
except StopIteration:
|
||||
fstatement = None
|
||||
if not isinstance(fstatement, ast.Assign) \
|
||||
or not _is_name_used(fstatement.value, modelname):
|
||||
break
|
||||
tregs = []
|
||||
for target in fstatement.targets:
|
||||
if isinstance(target, ast.Attribute) and target.attr == "store":
|
||||
if isinstance(target.value, ast.Name):
|
||||
tregs.append(self.symdict[target.value.id])
|
||||
else:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
raise NotImplementedError
|
||||
from_model.append((tregs, fstatement.value))
|
||||
|
||||
states, exit_states = gen_io(self, modelname, model, args, keywords, from_model)
|
||||
sa.assemble(states, exit_states)
|
||||
return fstatement
|
||||
|
||||
def visit_if(self, sa, node):
|
||||
test = self.ec.visit_expr(node.test)
|
||||
states_t, exit_states_t = self.visit_block(node.body)
|
||||
states_f, exit_states_f = self.visit_block(node.orelse)
|
||||
exit_states = exit_states_t + exit_states_f
|
||||
|
||||
test_state_stmt = If(test, id_next_state(states_t[0]))
|
||||
test_state = [test_state_stmt]
|
||||
if states_f:
|
||||
test_state_stmt.Else(id_next_state(states_f[0]))
|
||||
else:
|
||||
exit_states.append(test_state)
|
||||
|
||||
sa.assemble([test_state] + states_t + states_f,
|
||||
exit_states)
|
||||
|
||||
def visit_while(self, sa, node):
|
||||
test = self.ec.visit_expr(node.test)
|
||||
states_b, exit_states_b = self.visit_block(node.body)
|
||||
|
||||
test_state = [If(test, id_next_state(states_b[0]))]
|
||||
for exit_state in exit_states_b:
|
||||
exit_state.insert(0, id_next_state(test_state))
|
||||
|
||||
sa.assemble([test_state] + states_b, [test_state])
|
||||
|
||||
def visit_for(self, sa, node):
|
||||
if not isinstance(node.target, ast.Name):
|
||||
raise NotImplementedError
|
||||
target = node.target.id
|
||||
if target in self.symdict:
|
||||
raise NotImplementedError("For loop target must use an available name")
|
||||
it = self.visit_iterator(node.iter)
|
||||
states = []
|
||||
last_exit_states = []
|
||||
for iteration in it:
|
||||
self.symdict[target] = iteration
|
||||
states_b, exit_states_b = self.visit_block(node.body)
|
||||
for exit_state in last_exit_states:
|
||||
exit_state.insert(0, id_next_state(states_b[0]))
|
||||
last_exit_states = exit_states_b
|
||||
states += states_b
|
||||
del self.symdict[target]
|
||||
sa.assemble(states, last_exit_states)
|
||||
|
||||
def visit_iterator(self, node):
|
||||
return eval_ast(node, self.symdict)
|
||||
|
||||
def visit_expr_statement(self, sa, node):
|
||||
if isinstance(node.value, ast.Yield):
|
||||
yvalue = node.value.value
|
||||
if not isinstance(yvalue, ast.Call) or not isinstance(yvalue.func, ast.Name):
|
||||
raise NotImplementedError("Unrecognized I/O pattern")
|
||||
callee = self.symdict[yvalue.func.id]
|
||||
states, exit_states = gen_io(self, None, callee, yvalue.args, yvalue.keywords, [])
|
||||
sa.assemble(states, exit_states)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
class Pytholite(UnifiedIOObject):
|
||||
def __init__(self, func, *args, **kwargs):
|
||||
self.func = func
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def do_finalize(self):
|
||||
UnifiedIOObject.do_finalize(self)
|
||||
if self.get_dataflow():
|
||||
self.busy.reset = 1
|
||||
self.memory_ports = dict()
|
||||
for key in xdir(self):
|
||||
mem = getattr(self, key)
|
||||
if isinstance(mem, Memory):
|
||||
port = mem.get_port(write_capable=True, we_granularity=8)
|
||||
self.specials += port
|
||||
self.memory_ports[mem] = port
|
||||
self._compile()
|
||||
|
||||
def _compile(self):
|
||||
tree = ast.parse(inspect.getsource(self.func))
|
||||
symdict = self.func.__globals__.copy()
|
||||
registers = []
|
||||
|
||||
states = _Compiler(self, symdict, registers).visit_top(tree, self.args, self.kwargs)
|
||||
|
||||
for register in registers:
|
||||
if register.source_encoding:
|
||||
register.finalize()
|
||||
self.submodules += register
|
||||
|
||||
fsm = implement_fsm(states)
|
||||
self.submodules += TransformModule(LowerAbstractLoad().visit, fsm)
|
|
@ -1,122 +0,0 @@
|
|||
import ast
|
||||
|
||||
from migen.fhdl.structure import *
|
||||
from migen.fhdl.structure import _Slice
|
||||
from migen.pytholite import transel
|
||||
from migen.pytholite.reg import *
|
||||
from migen.pytholite.util import eval_ast
|
||||
|
||||
class ExprCompiler:
|
||||
def __init__(self, symdict):
|
||||
self.symdict = symdict
|
||||
|
||||
def visit_expr(self, node):
|
||||
# Attempt compile-time evaluation first
|
||||
try:
|
||||
result = eval_ast(node, self.symdict)
|
||||
except:
|
||||
result = None
|
||||
if isinstance(result, int):
|
||||
return result
|
||||
|
||||
if isinstance(node, ast.Call):
|
||||
return self.visit_expr_call(node)
|
||||
elif isinstance(node, ast.BinOp):
|
||||
return self.visit_expr_binop(node)
|
||||
elif isinstance(node, ast.Compare):
|
||||
return self.visit_expr_compare(node)
|
||||
elif isinstance(node, ast.Name):
|
||||
return self.visit_expr_name(node)
|
||||
elif isinstance(node, ast.Num):
|
||||
return self.visit_expr_num(node)
|
||||
elif isinstance(node, ast.Attribute):
|
||||
return self.visit_expr_attribute(node)
|
||||
elif isinstance(node, ast.Subscript):
|
||||
return self.visit_expr_subscript(node)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def visit_expr_call(self, node):
|
||||
if isinstance(node.func, ast.Name):
|
||||
callee = self.symdict[node.func.id]
|
||||
else:
|
||||
raise NotImplementedError
|
||||
if callee == transel.bitslice:
|
||||
if len(node.args) != 2 and len(node.args) != 3:
|
||||
raise TypeError("bitslice() takes 2 or 3 arguments")
|
||||
val = self.visit_expr(node.args[0])
|
||||
low = eval_ast(node.args[1], self.symdict)
|
||||
if len(node.args) == 3:
|
||||
up = eval_ast(node.args[2], self.symdict)
|
||||
else:
|
||||
up = low + 1
|
||||
return _Slice(val, low, up)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def visit_expr_binop(self, node):
|
||||
left = self.visit_expr(node.left)
|
||||
right = self.visit_expr(node.right)
|
||||
if isinstance(node.op, ast.Add):
|
||||
return left + right
|
||||
elif isinstance(node.op, ast.Sub):
|
||||
return left - right
|
||||
elif isinstance(node.op, ast.Mult):
|
||||
return left * right
|
||||
elif isinstance(node.op, ast.LShift):
|
||||
return left << right
|
||||
elif isinstance(node.op, ast.RShift):
|
||||
return left >> right
|
||||
elif isinstance(node.op, ast.BitOr):
|
||||
return left | right
|
||||
elif isinstance(node.op, ast.BitXor):
|
||||
return left ^ right
|
||||
elif isinstance(node.op, ast.BitAnd):
|
||||
return left & right
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def visit_expr_compare(self, node):
|
||||
test = self.visit_expr(node.left)
|
||||
r = None
|
||||
for op, rcomparator in zip(node.ops, node.comparators):
|
||||
comparator = self.visit_expr(rcomparator)
|
||||
if isinstance(op, ast.Eq):
|
||||
comparison = test == comparator
|
||||
elif isinstance(op, ast.NotEq):
|
||||
comparison = test != comparator
|
||||
elif isinstance(op, ast.Lt):
|
||||
comparison = test < comparator
|
||||
elif isinstance(op, ast.LtE):
|
||||
comparison = test <= comparator
|
||||
elif isinstance(op, ast.Gt):
|
||||
comparison = test > comparator
|
||||
elif isinstance(op, ast.GtE):
|
||||
comparison = test >= comparator
|
||||
else:
|
||||
raise NotImplementedError
|
||||
if r is None:
|
||||
r = comparison
|
||||
else:
|
||||
r = r & comparison
|
||||
test = comparator
|
||||
return r
|
||||
|
||||
def visit_expr_name(self, node):
|
||||
if node.id == "True":
|
||||
return 1
|
||||
if node.id == "False":
|
||||
return 0
|
||||
r = self.symdict[node.id]
|
||||
if isinstance(r, ImplRegister):
|
||||
r = r.storage
|
||||
return r
|
||||
|
||||
def visit_expr_num(self, node):
|
||||
return node.n
|
||||
|
||||
def visit_expr_attribute(self, node):
|
||||
raise NotImplementedError
|
||||
|
||||
def visit_expr_subscript(self, node):
|
||||
raise NotImplementedError
|
|
@ -1,201 +0,0 @@
|
|||
import ast
|
||||
from itertools import zip_longest
|
||||
|
||||
from migen.fhdl.std import *
|
||||
from migen.flow.actor import Source, Sink
|
||||
from migen.flow.transactions import *
|
||||
from migen.bus import wishbone
|
||||
from migen.bus.transactions import *
|
||||
from migen.pytholite.util import *
|
||||
from migen.pytholite.expr import ExprCompiler
|
||||
|
||||
class _TokenPullExprCompiler(ExprCompiler):
|
||||
def __init__(self, symdict, modelname, ep):
|
||||
ExprCompiler.__init__(self, symdict)
|
||||
self.modelname = modelname
|
||||
self.ep = ep
|
||||
|
||||
def visit_expr_subscript(self, node):
|
||||
# check that we are subscripting <modelname>.value
|
||||
if not isinstance(node.value, ast.Attribute) \
|
||||
or node.value.attr != "value" \
|
||||
or not isinstance(node.value.value, ast.Name) \
|
||||
or node.value.value.id != self.modelname:
|
||||
raise NotImplementedError
|
||||
|
||||
if not isinstance(node.slice, ast.Index):
|
||||
raise NotImplementedError
|
||||
field = eval_ast(node.slice.value, self.symdict)
|
||||
signal = getattr(self.ep.payload, field)
|
||||
|
||||
return signal
|
||||
|
||||
def _gen_df_io(compiler, modelname, to_model, from_model):
|
||||
epname = eval_ast(to_model["endpoint"], compiler.symdict)
|
||||
values = to_model["value"]
|
||||
idle_wait = eval_ast(to_model["idle_wait"], compiler.symdict)
|
||||
ep = getattr(compiler.ioo, epname)
|
||||
if idle_wait:
|
||||
state = [compiler.ioo.busy.eq(0)]
|
||||
else:
|
||||
state = []
|
||||
|
||||
if isinstance(values, ast.Name) and values.id == "None":
|
||||
# token pull from sink
|
||||
if not isinstance(ep, Sink):
|
||||
raise TypeError("Attempted to pull from source")
|
||||
ec = _TokenPullExprCompiler(compiler.symdict, modelname, ep)
|
||||
for target_regs, expr in from_model:
|
||||
cexpr = ec.visit_expr(expr)
|
||||
state += [reg.load(cexpr) for reg in target_regs]
|
||||
state += [
|
||||
ep.ack.eq(1),
|
||||
If(~ep.stb, id_next_state(state))
|
||||
]
|
||||
return [state], [state]
|
||||
else:
|
||||
# token push to source
|
||||
if not isinstance(ep, Source):
|
||||
raise TypeError("Attempted to push to sink")
|
||||
if from_model:
|
||||
raise TypeError("Attempted to read from pushed token")
|
||||
if not isinstance(values, ast.Dict):
|
||||
raise NotImplementedError
|
||||
for akey, value in zip(values.keys, values.values):
|
||||
key = eval_ast(akey, compiler.symdict)
|
||||
signal = getattr(ep.payload, key)
|
||||
state.append(signal.eq(compiler.ec.visit_expr(value)))
|
||||
state += [
|
||||
ep.stb.eq(1),
|
||||
If(~ep.ack, id_next_state(state))
|
||||
]
|
||||
return [state], [state]
|
||||
|
||||
class _BusReadExprCompiler(ExprCompiler):
|
||||
def __init__(self, symdict, modelname, data_signal):
|
||||
ExprCompiler.__init__(self, symdict)
|
||||
self.modelname = modelname
|
||||
self.data_signal = data_signal
|
||||
|
||||
def visit_expr_attribute(self, node):
|
||||
# recognize <modelname>.data as the bus read signal, raise exception otherwise
|
||||
if not isinstance(node.value, ast.Name) \
|
||||
or node.value.id != self.modelname \
|
||||
or node.attr != "data":
|
||||
raise NotImplementedError
|
||||
return self.data_signal
|
||||
|
||||
def _gen_wishbone_io(compiler, modelname, model, to_model, from_model, bus):
|
||||
state = [
|
||||
bus.cyc.eq(1),
|
||||
bus.stb.eq(1),
|
||||
bus.adr.eq(compiler.ec.visit_expr(to_model["address"])),
|
||||
]
|
||||
|
||||
if model == TWrite:
|
||||
if from_model:
|
||||
raise TypeError("Attempted to read from write transaction")
|
||||
state += [
|
||||
bus.we.eq(1),
|
||||
bus.dat_w.eq(compiler.ec.visit_expr(to_model["data"]))
|
||||
]
|
||||
sel = to_model["sel"]
|
||||
if isinstance(sel, ast.Name) and sel.id == "None":
|
||||
nbytes = (flen(bus.dat_w) + 7)//8
|
||||
state.append(bus.sel.eq(2**nbytes-1))
|
||||
else:
|
||||
state.append(bus.sel.eq(compiler.ec.visit_expr(sel)))
|
||||
else:
|
||||
ec = _BusReadExprCompiler(compiler.symdict, modelname, bus.dat_r)
|
||||
for target_regs, expr in from_model:
|
||||
cexpr = ec.visit_expr(expr)
|
||||
state += [reg.load(cexpr) for reg in target_regs]
|
||||
state.append(If(~bus.ack, id_next_state(state)))
|
||||
return [state], [state]
|
||||
|
||||
def _gen_memory_io(compiler, modelname, model, to_model, from_model, port):
|
||||
s1 = [port.adr.eq(compiler.ec.visit_expr(to_model["address"]))]
|
||||
if model == TWrite:
|
||||
if from_model:
|
||||
raise TypeError("Attempted to read from write transaction")
|
||||
s1.append(port.dat_w.eq(compiler.ec.visit_expr(to_model["data"])))
|
||||
sel = to_model["sel"]
|
||||
if isinstance(sel, ast.Name) and sel.id == "None":
|
||||
nbytes = (flen(port.dat_w) + 7)//8
|
||||
s1.append(port.we.eq(2**nbytes-1))
|
||||
else:
|
||||
s1.append(port.we.eq(compiler.ec.visit_expr(sel)))
|
||||
return [s1], [s1]
|
||||
else:
|
||||
s2 = []
|
||||
s1.append(id_next_state(s2))
|
||||
ec = _BusReadExprCompiler(compiler.symdict, modelname, port.dat_r)
|
||||
for target_regs, expr in from_model:
|
||||
cexpr = ec.visit_expr(expr)
|
||||
s2 += [reg.load(cexpr) for reg in target_regs]
|
||||
return [s1, s2], [s2]
|
||||
|
||||
def _gen_bus_io(compiler, modelname, model, to_model, from_model):
|
||||
busname = eval_ast(to_model["busname"], compiler.symdict)
|
||||
if busname is None:
|
||||
buses = compiler.ioo.get_buses()
|
||||
if len(buses) != 1:
|
||||
raise TypeError("Bus name not specified")
|
||||
bus = list(buses.values())[0]
|
||||
else:
|
||||
bus = getattr(compiler.ioo, busname)
|
||||
if isinstance(bus, wishbone.Interface):
|
||||
return _gen_wishbone_io(compiler, modelname, model, to_model, from_model, bus)
|
||||
elif isinstance(bus, Memory):
|
||||
port = compiler.ioo.memory_ports[bus]
|
||||
return _gen_memory_io(compiler, modelname, model, to_model, from_model, port)
|
||||
else:
|
||||
raise NotImplementedError("Unsupported bus")
|
||||
|
||||
def _decode_args(desc, args, args_kw):
|
||||
d = {}
|
||||
argnames = set()
|
||||
for param, value in zip_longest(desc, args):
|
||||
if param is None:
|
||||
raise TypeError("Too many arguments")
|
||||
if isinstance(param, tuple):
|
||||
name, default = param
|
||||
else:
|
||||
name, default = param, None
|
||||
|
||||
# build the set of argument names at the same time
|
||||
argnames.add(name)
|
||||
|
||||
if value is None:
|
||||
if default is None:
|
||||
raise TypeError("No default value for parameter " + name)
|
||||
else:
|
||||
d[name] = default
|
||||
else:
|
||||
d[name] = value
|
||||
for akw in args_kw:
|
||||
if akw.arg not in argnames:
|
||||
raise TypeError("Parameter " + akw.arg + " does not exist")
|
||||
d[akw.arg] = akw.value
|
||||
return d
|
||||
|
||||
def gen_io(compiler, modelname, model, to_model, to_model_kw, from_model):
|
||||
if model == Token:
|
||||
desc = [
|
||||
"endpoint",
|
||||
("value", ast.Name("None", ast.Load(), lineno=0, col_offset=0)),
|
||||
("idle_wait", ast.Name("False", ast.Load(), lineno=0, col_offset=0))
|
||||
]
|
||||
args = _decode_args(desc, to_model, to_model_kw)
|
||||
return _gen_df_io(compiler, modelname, args, from_model)
|
||||
elif model == TRead or model == TWrite:
|
||||
desc = [
|
||||
"address",
|
||||
("data", ast.Num(0)),
|
||||
("sel", ast.Name("None", ast.Load(), lineno=0, col_offset=0)),
|
||||
("busname", ast.Name("None", ast.Load(), lineno=0, col_offset=0))
|
||||
]
|
||||
args = _decode_args(desc, to_model, to_model_kw)
|
||||
return _gen_bus_io(compiler, modelname, model, args, from_model)
|
||||
else:
|
||||
raise NotImplementedError
|
|
@ -1,41 +0,0 @@
|
|||
from operator import itemgetter
|
||||
|
||||
from migen.fhdl.std import *
|
||||
from migen.fhdl import visit as fhdl
|
||||
|
||||
class AbstractLoad:
|
||||
def __init__(self, target, source):
|
||||
self.target = target
|
||||
self.source = source
|
||||
|
||||
def lower(self):
|
||||
if not self.target.finalized:
|
||||
raise FinalizeError
|
||||
return self.target.sel.eq(self.target.source_encoding[id(self.source)])
|
||||
|
||||
class LowerAbstractLoad(fhdl.NodeTransformer):
|
||||
def visit_unknown(self, node):
|
||||
if isinstance(node, AbstractLoad):
|
||||
return node.lower()
|
||||
else:
|
||||
return node
|
||||
|
||||
class ImplRegister(Module):
|
||||
def __init__(self, name, bits_sign):
|
||||
self.name = name
|
||||
self.storage = Signal(bits_sign, name=self.name)
|
||||
self.source_encoding = {}
|
||||
self.id_to_source = {}
|
||||
|
||||
def load(self, source):
|
||||
if id(source) not in self.source_encoding:
|
||||
self.source_encoding[id(source)] = len(self.source_encoding) + 1
|
||||
self.id_to_source[id(source)] = source
|
||||
return AbstractLoad(self, source)
|
||||
|
||||
def do_finalize(self):
|
||||
self.sel = Signal(max=len(self.source_encoding)+1, name="pl_regsel_"+self.name)
|
||||
# do nothing when sel == 0
|
||||
items = sorted(self.source_encoding.items(), key=itemgetter(1))
|
||||
cases = dict((v, self.storage.eq(self.id_to_source[k])) for k, v in items)
|
||||
self.sync += Case(self.sel, cases)
|
|
@ -1,198 +0,0 @@
|
|||
import operator
|
||||
|
||||
def bitslice(val, low, up=None):
|
||||
if up is None:
|
||||
up = low + 1
|
||||
nbits = up - low
|
||||
mask = (2**nbits - 1) << low
|
||||
return (val & mask) >> low
|
||||
|
||||
class Register:
|
||||
def __init__(self, bits_sign):
|
||||
if isinstance(bits_sign, tuple):
|
||||
self._nbits, self._signed = bits_sign
|
||||
else:
|
||||
self._nbits, self._signed = bits_sign, False
|
||||
self._val = 0
|
||||
|
||||
def _set_store(self, val):
|
||||
if self._signed:
|
||||
sbw = 2**(self._nbits - 1)
|
||||
self._val = val & (sbw - 1)
|
||||
if val & sbw:
|
||||
self._val -= sbw
|
||||
else:
|
||||
self._val = val & (2**self._nbits - 1)
|
||||
store = property(None, _set_store)
|
||||
|
||||
def __nonzero__(self):
|
||||
if self._val:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def __len__(self):
|
||||
return self._nbits
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val + other._val
|
||||
else:
|
||||
return self._val + other
|
||||
def __radd__(self, other):
|
||||
return other + self._val
|
||||
|
||||
def __sub__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val - other._val
|
||||
else:
|
||||
return self._val - other
|
||||
def __rsub__(self, other):
|
||||
return other - self._val
|
||||
|
||||
def __mul__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val * other._val
|
||||
else:
|
||||
return self._val * other
|
||||
def __rmul__(self, other):
|
||||
return other * self._val
|
||||
|
||||
def __div__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val / other._val
|
||||
else:
|
||||
return self._val / other
|
||||
def __rdiv__(self, other):
|
||||
return other / self._val
|
||||
|
||||
def __truediv__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return operator.truediv(self._val, other._val)
|
||||
else:
|
||||
return operator.truediv(self._val, other)
|
||||
def __rtruediv__(self, other):
|
||||
return operator.truediv(other, self._val)
|
||||
|
||||
def __floordiv__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val // other._val
|
||||
else:
|
||||
return self._val // other
|
||||
def __rfloordiv__(self, other):
|
||||
return other // self._val
|
||||
|
||||
def __mod__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val % other._val
|
||||
else:
|
||||
return self._val % other
|
||||
def __rmod__(self, other):
|
||||
return other % self._val
|
||||
|
||||
def __pow__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val ** other._val
|
||||
else:
|
||||
return self._val ** other
|
||||
def __rpow__(self, other):
|
||||
return other ** self._val
|
||||
|
||||
def __lshift__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val << other._val
|
||||
else:
|
||||
return self._val << other
|
||||
def __rlshift__(self, other):
|
||||
return other << self._val
|
||||
|
||||
def __rshift__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val >> other._val
|
||||
else:
|
||||
return self._val >> other
|
||||
def __rrshift__(self, other):
|
||||
return other >> self._val
|
||||
|
||||
def __and__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val & other._val
|
||||
else:
|
||||
return self._val & other
|
||||
def __rand__(self, other):
|
||||
return other & self._val
|
||||
|
||||
def __or__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val | other._val
|
||||
else:
|
||||
return self._val | other
|
||||
def __ror__(self, other):
|
||||
return other | self._val
|
||||
|
||||
def __xor__(self, other):
|
||||
if isinstance(other, Register):
|
||||
return self._val ^ other._val
|
||||
else:
|
||||
return self._val ^ other
|
||||
def __rxor__(self, other):
|
||||
return other ^ self._val
|
||||
|
||||
def __neg__(self):
|
||||
return -self._val
|
||||
|
||||
def __pos__(self):
|
||||
return +self._val
|
||||
|
||||
def __abs__(self):
|
||||
return abs(self._val)
|
||||
|
||||
def __invert__(self):
|
||||
return ~self._val
|
||||
|
||||
def __int__(self):
|
||||
return int(self._val)
|
||||
|
||||
def __float__(self):
|
||||
return float(self._val)
|
||||
|
||||
def __oct__(self):
|
||||
return oct(self._val)
|
||||
|
||||
def __hex__(self):
|
||||
return hex(self._val)
|
||||
|
||||
def __index__(self):
|
||||
return int(self._val)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._val < other
|
||||
|
||||
def __le__(self, other):
|
||||
return self._val <= other
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._val == other
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._val >= other
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._val > other
|
||||
|
||||
def __ne__(self, other):
|
||||
return self._val != other
|
||||
|
||||
def __str__(self):
|
||||
return str(self._val)
|
||||
|
||||
def __repr__(self):
|
||||
return "Register(" + repr(self._val) + ")"
|
||||
|
||||
def _augm(self, other):
|
||||
raise TypeError("Register objects do not support augmented assignment")
|
||||
__iadd__ = __isub__ = __idiv__ = __imul__ = __ipow__ = __imod__ = _augm
|
||||
__ior__ = __iand__ = __ixor__ = __irshift__ = __ilshift__ = _augm
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
raise TypeError("Register objects do not support item/slice assignment")
|
|
@ -1,33 +0,0 @@
|
|||
import ast
|
||||
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
|
||||
def id_next_state(l):
|
||||
return NextState(id(l))
|
||||
|
||||
# entry state is first state returned
|
||||
class StateAssembler:
|
||||
def __init__(self):
|
||||
self.states = []
|
||||
self.exit_states = []
|
||||
|
||||
def assemble(self, n_states, n_exit_states):
|
||||
self.states += n_states
|
||||
for exit_state in self.exit_states:
|
||||
exit_state.insert(0, id_next_state(n_states[0]))
|
||||
self.exit_states = n_exit_states
|
||||
|
||||
def ret(self):
|
||||
return self.states, self.exit_states
|
||||
|
||||
def implement_fsm(states):
|
||||
fsm = FSM()
|
||||
for state in states:
|
||||
fsm.act(id(state), state)
|
||||
return fsm
|
||||
|
||||
def eval_ast(expr, symdict):
|
||||
if not isinstance(expr, ast.Expression):
|
||||
expr = ast.Expression(expr)
|
||||
code = compile(expr, "<ast>", "eval")
|
||||
return eval(code, symdict)
|
Loading…
Reference in a new issue