6 from collections
import OrderedDict
7 from collections
.abc
import Mapping
9 from nmigen
import tracer
10 from nmigen
.utils
import log2_int
11 from nmigen
.build
.run
import *
13 from nmigen_soc
.memory
import MemoryMap
14 from nmigen_soc
.periph
import ConstantMap
, ConstantBool
, ConstantInt
16 from .. import __version__
, software
17 from ..periph
import Peripheral
20 __all__
= ["socproperty", "ConstantAddr", "ConstantMapCollection", "SoC", "ConfigBuilder"]
23 def socproperty(cls
, *, weak
=False, src_loc_at
=0):
24 name
= tracer
.get_var_name(depth
=2 + src_loc_at
)
25 __name
= "__{}".format(name
)
28 assert isinstance(self
, SoC
)
29 attr
= getattr(self
, __name
, None)
30 if attr
is None and not weak
:
31 raise NotImplementedError("SoC {!r} does not have a {}"
35 def setter(self
, value
):
36 assert isinstance(self
, SoC
)
37 if not isinstance(value
, cls
):
38 raise TypeError("{} must be an instance of {}, not {!r}"
39 .format(name
, cls
.__name
__, value
))
40 setattr(self
, __name
, value
)
42 return property(getter
, setter
)
45 class ConstantAddr(ConstantInt
):
46 def __init__(self
, value
, *, width
=None):
47 return super().__init
__(value
, width
=width
, signed
=False)
50 return "ConstantAddr({}, width={})".format(self
.value
, self
.width
)
53 class ConstantMapCollection(Mapping
):
54 def __init__(self
, **constant_maps
):
55 self
._storage
= OrderedDict()
56 for key
, value
in constant_maps
.items():
59 elif not isinstance(value
, (ConstantMap
, ConstantMapCollection
)):
60 raise TypeError("Constant map must be an instance of ConstantMap or "
61 "ConstantMapCollection, not {!r}"
63 self
._storage
[key
] = value
65 def flatten(self
, *, prefix
="", separator
="_"):
66 if not isinstance(prefix
, str):
67 raise TypeError("Prefix must be a string, not {!r}".format(prefix
))
68 if not isinstance(separator
, str):
69 raise TypeError("Separator must be a string, not {!r}".format(separator
))
70 for key
, value
in self
.items():
71 if isinstance(value
, ConstantMap
):
72 for const_key
, const_value
in value
.items():
73 yield f
"{prefix}{key}{separator}{const_key}", const_value
74 elif isinstance(value
, ConstantMapCollection
):
75 yield from value
.flatten(prefix
=f
"{prefix}{key}{separator}", separator
=separator
)
77 def union(self
, **other
):
79 for key
in self
.keys() | other
.keys():
80 self_value
= self
.get(key
, None)
81 other_value
= other
.get(key
, None)
82 if self_value
is None or other_value
is None:
83 union
[key
] = self_value
or other_value
84 elif isinstance(self_value
, ConstantMap
):
85 if not isinstance(other_value
, ConstantMap
):
86 raise TypeError # TODO
87 union
[key
] = ConstantMap(**self_value
, **other_value
)
88 elif isinstance(self_value
, ConstantMapCollection
):
89 if not isinstance(other_value
, ConstantMapCollection
):
90 raise TypeError # TODO
91 union
[key
] = self_value
.merge(**{key
: other_value
})
94 return ConstantMapCollection(**union
)
96 def __getitem__(self
, prefix
):
97 return self
._storage
[prefix
]
100 yield from self
._storage
103 return len(self
._storage
)
106 return "ConstantMapCollection({})".format(list(self
._storage
.items()))
110 memory_map
= socproperty(MemoryMap
)
111 constants
= socproperty(ConstantMapCollection
, weak
=True)
113 def build(self
, build_dir
="build/soc", do_build
=True, name
=None):
114 plan
= ConfigBuilder().prepare(self
, build_dir
, name
)
118 products
= plan
.execute_local(build_dir
)
124 "build_{{name}}.sh": r
"""
129 "{{name}}_resources.csv": r
"""
131 # <resource name>, <start address>, <end address>, <access width>
132 {% for res_info in soc.memory_map.all_resources() -%}
133 {{"_".join(res_info.name)}}, {{hex(res_info.start)}}, {{hex(res_info.end)}}, {{res_info.width}}
137 command_templates
= []
139 def prepare(self
, soc
, build_dir
, name
, **render_params
):
140 name
= name
or type(soc
).__name
__.lower()
142 autogenerated
= "Automatically generated by LambdaSoC {}. Do not edit.".format(__version__
)
144 def periph_addr(periph
):
145 assert isinstance(periph
, Peripheral
)
146 periph_map
= periph
.bus
.memory_map
147 for window
, (start
, end
, ratio
) in soc
.memory_map
.windows():
148 if periph_map
is window
:
150 raise KeyError(periph
)
152 def periph_size(periph
):
153 assert isinstance(periph
, Peripheral
)
154 granularity_bits
= log2_int(periph
.bus
.data_width
// periph
.bus
.granularity
)
155 return 2**(periph
.bus
.addr_width
+ granularity_bits
)
159 for index
, command_tpl
in enumerate(self
.command_templates
):
160 command
= render(command_tpl
, origin
="<command#{}>".format(index
+ 1))
161 command
= re
.sub(r
"\s+", " ", command
)
162 commands
.append(command
)
163 return "\n".join(commands
)
165 def render(source
, origin
):
167 source
= textwrap
.dedent(source
).strip()
168 compiled
= jinja2
.Template(source
, trim_blocks
=True, lstrip_blocks
=True)
169 except jinja2
.TemplateSyntaxError
as e
:
170 e
.args
= ("{} (at {}:{})".format(e
.message
, origin
, e
.lineno
),)
172 return compiled
.render({
173 "autogenerated": autogenerated
,
174 "build_dir": os
.path
.abspath(build_dir
),
175 "emit_commands": emit_commands
,
178 "periph_addr": periph_addr
,
179 "periph_size": periph_size
,
181 "software_dir": os
.path
.dirname(software
.__file
__),
185 plan
= BuildPlan(script
="build_{}".format(name
))
186 for filename_tpl
, content_tpl
in self
.file_templates
.items():
187 plan
.add_file(render(filename_tpl
, origin
=filename_tpl
),
188 render(content_tpl
, origin
=content_tpl
))