pytholite/io: support Wishbone writes
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Fri, 23 Nov 2012 11:41:50 +0000 (12:41 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Fri, 23 Nov 2012 11:41:50 +0000 (12:41 +0100)
migen/pytholite/io.py

index 89effa6376fa761eb9fb5c4dc7565e704d618093..04fb171eecb5d42e1bb93b2387c521a34834483e 100644 (file)
@@ -1,8 +1,10 @@
 import ast
+from itertools import zip_longest
 
 from migen.fhdl.structure import *
 from migen.uio.ioo import UnifiedIOObject
 from migen.actorlib.sim import *
+from migen.bus import wishbone
 from migen.bus.transactions import *
 from migen.pytholite.fsm import *
 from migen.pytholite.expr import ExprCompiler
@@ -32,14 +34,12 @@ class _TokenPullExprCompiler(ExprCompiler):
                
                return signal
 
-def gen_df_io(compiler, modelname, to_model, from_model):
-       if len(to_model) == 1 or len(to_model) == 2:
-               epname = ast.literal_eval(to_model[0])
-               ep = compiler.ioo.endpoints[epname]
-       else:
-               raise TypeError("Token() takes 1 or 2 arguments")
+def _gen_df_io(compiler, modelname, to_model, from_model):
+       epname = ast.literal_eval(to_model["endpoint"])
+       values = to_model["value"]
+       ep = compiler.ioo.endpoints[epname]
        
-       if len(to_model) == 1:
+       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")
@@ -59,11 +59,10 @@ def gen_df_io(compiler, modelname, to_model, from_model):
                        raise TypeError("Attempted to push to sink")
                if from_model:
                        raise TypeError("Attempted to read from pushed token")
-               d = to_model[1]
-               if not isinstance(d, ast.Dict):
+               if not isinstance(values, ast.Dict):
                        raise NotImplementedError
                state = []
-               for akey, value in zip(d.keys, d.values):
+               for akey, value in zip(values.keys, values.values):
                        key = ast.literal_eval(akey)
                        signal = getattr(ep.token, key)
                        state.append(signal.eq(compiler.ec.visit_expr(value)))
@@ -73,8 +72,79 @@ def gen_df_io(compiler, modelname, to_model, from_model):
                ]
                return [state], [state]
 
-def gen_io(compiler, modelname, model, to_model, from_model):
+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:
+               state.append(bus.we.eq(1))
+               if from_model:
+                       raise TypeError("Attempted to read from write transaction")
+       else:
+               state.append(bus.we.eq(0))
+               # TODO
+       state.append(If(~bus.ack, AbstractNextState(state)))
+       return [state], [state]
+
+def _gen_bus_io(compiler, modelname, model, to_model, from_model):
+       busname = ast.literal_eval(to_model["busname"])
+       if busname is None:
+               if len(compiler.ioo.buses) != 1:
+                       raise TypeError("Bus name not specified")
+               bus = list(compiler.ioo.buses.values())[0]
+       else:
+               bus = compiler.ioo.buses[busname]
+       if isinstance(bus, wishbone.Interface):
+               return _gen_wishbone_io(compiler, modelname, model, to_model, from_model, bus)
+       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:
-               return gen_df_io(compiler, modelname, to_model, from_model)
+               desc = [
+                       "endpoint",
+                       ("value", ast.Name("None", ast.Load())),
+               ]
+               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())),
+                       ("busname", ast.Name("None", ast.Load()))
+               ]
+               args = _decode_args(desc, to_model, to_model_kw)
+               return _gen_bus_io(compiler, modelname, model, args, from_model)
        else:
                raise NotImplementedError