synthesize.py
1 #!/usr/bin/env python3 2 3 from collections.abc import Callable 4 import os 5 import sys 6 import argparse 7 8 from amaranth.build import Platform 9 from amaranth.build.res import PortGroup 10 from amaranth import * 11 from amaranth.lib import memory 12 from amaranth.lib.wiring import Component, Flow, Out, connect, flipped 13 from amaranth_types import AbstractInterface 14 15 if __name__ == "__main__": 16 parent = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 sys.path.insert(0, parent) 18 19 20 from transactron.utils.dependencies import DependencyContext, DependencyManager 21 from transactron.utils import ModuleConnector 22 from coreblocks.params.genparams import GenParams 23 from coreblocks.params.fu_params import FunctionalComponentParams 24 from coreblocks.core import Core 25 from coreblocks.func_blocks.fu.alu import ALUComponent 26 from coreblocks.func_blocks.fu.div_unit import DivComponent 27 from coreblocks.func_blocks.fu.mul_unit import MulComponent, MulType 28 from coreblocks.func_blocks.fu.shift_unit import ShiftUnitComponent 29 from coreblocks.func_blocks.fu.zbc import ZbcComponent 30 from coreblocks.func_blocks.fu.zbs import ZbsComponent 31 from transactron import TransactronContextElaboratable 32 from transactron.lib import Adapter, AdapterTrans 33 from transactron.utils.amaranth_ext.memory import MultiportXORMemory, MultiportXORILVTMemory, MultiportOneHotILVTMemory 34 from coreblocks.peripherals.wishbone import WishboneArbiter, WishboneInterface 35 from constants.ecp5_platforms import ( 36 ResourceBuilder, 37 append_resources, 38 signature_resources, 39 make_ecp5_platform, 40 ) 41 42 from coreblocks.params.configurations import * 43 44 str_to_coreconfig: dict[str, CoreConfiguration] = { 45 "basic": basic_core_config, 46 "tiny": tiny_core_config, 47 "full": full_core_config, 48 } 49 50 51 class InterfaceConnector(Elaboratable): 52 def __init__(self, interface: AbstractInterface, name: str, number: int): 53 self.interface = interface 54 self.name = name 55 self.number = number 56 57 @staticmethod 58 def with_resources(interface: AbstractInterface, name: str, number: int): 59 connector = InterfaceConnector(interface, name, number) 60 resources = signature_resources(interface.signature, name, number) 61 return connector, resources 62 63 def elaborate(self, platform: Platform): 64 m = Module() 65 66 pins = platform.request(self.name, self.number) 67 assert isinstance(pins, PortGroup) 68 69 for hier_name, member, v in self.interface.signature.flatten(self.interface): 70 name = "__".join(str(x) for x in hier_name) 71 if member.flow == Flow.Out: 72 m.d.comb += getattr(pins, name).o.eq(v) 73 else: 74 m.d.comb += v.eq(getattr(pins, name).i) 75 76 return m 77 78 79 UnitCore = Callable[[GenParams], tuple[ResourceBuilder, Elaboratable]] 80 81 82 class SynthesisCore(Component): 83 wb: WishboneInterface 84 85 def __init__(self, gen_params: GenParams): 86 super().__init__({"wb": Out(WishboneInterface(gen_params.wb_params).signature)}) 87 self.gen_params = gen_params 88 89 def elaborate(self, platform): 90 m = Module() 91 92 m.submodules.core = core = Core(gen_params=self.gen_params) 93 m.submodules.wb_arbiter = wb_arbiter = WishboneArbiter(self.gen_params.wb_params, 2) 94 95 connect(m, wb_arbiter.masters[0], core.wb_instr) 96 connect(m, wb_arbiter.masters[1], core.wb_data) 97 connect(m, flipped(self.wb), wb_arbiter.slave_wb) 98 99 return m 100 101 102 def unit_core(gen_params: GenParams): 103 core = SynthesisCore(gen_params) 104 105 connector, resources = InterfaceConnector.with_resources(core, "wishbone", 0) 106 107 module = ModuleConnector(core=core, connector=connector) 108 109 return resources, TransactronContextElaboratable(module, dependency_manager=DependencyContext.get()) 110 111 112 def unit_fu(unit_params: FunctionalComponentParams): 113 def unit(gen_params: GenParams): 114 fu = unit_params.get_module(gen_params) 115 issue_adapter = AdapterTrans.create(fu.issue) 116 push_result_adapter = Adapter.create(fu.push_result) 117 118 issue_connector, issue_resources = InterfaceConnector.with_resources(issue_adapter, "adapter", 0) 119 push_connector, push_resources = InterfaceConnector.with_resources(push_result_adapter, "adapter", 1) 120 121 resources = append_resources(issue_resources, push_resources) 122 123 module = ModuleConnector( 124 fu=fu, 125 issue_connector=issue_connector, 126 accept_connector=push_connector, 127 issue_adapter=issue_adapter, 128 accept_adapter=push_result_adapter, 129 ) 130 131 return resources, TransactronContextElaboratable(module, dependency_manager=DependencyContext.get()) 132 133 return unit 134 135 136 core_units = { 137 "core": unit_core, 138 "alu_basic": unit_fu(ALUComponent(zba_enable=False, zbb_enable=False, zicond_enable=False)), 139 "alu_full": unit_fu(ALUComponent(zba_enable=True, zbb_enable=True, zicond_enable=True)), 140 "mul_shift": unit_fu(MulComponent(MulType.SHIFT_MUL)), 141 "mul_sequence": unit_fu(MulComponent(MulType.SEQUENCE_MUL)), 142 "mul_pipelined": unit_fu(MulComponent(MulType.PIPELINED_MUL)), 143 "mul_recursive": unit_fu(MulComponent(MulType.RECURSIVE_MUL)), 144 "div": unit_fu(DivComponent()), 145 "shift_basic": unit_fu(ShiftUnitComponent(zbb_enable=False)), 146 "shift_full": unit_fu(ShiftUnitComponent(zbb_enable=True)), 147 "zbs": unit_fu(ZbsComponent()), 148 "zbc": unit_fu(ZbcComponent()), 149 } 150 151 152 memory_types = { 153 "plain": memory.Memory, 154 "xor": MultiportXORMemory, 155 "xor-ilvt": MultiportXORILVTMemory, 156 "onehot-ilvt": MultiportOneHotILVTMemory, 157 } 158 159 160 def synthesize(core_config: CoreConfiguration, platform: str, core: UnitCore): 161 with DependencyContext(DependencyManager()): 162 gen_params = GenParams(core_config) 163 resource_builder, module = core(gen_params) 164 165 if platform == "ecp5": 166 make_ecp5_platform(resource_builder)().build(module) 167 168 169 def main(): 170 parser = argparse.ArgumentParser() 171 parser.add_argument( 172 "-p", 173 "--platform", 174 default="ecp5", 175 choices=["ecp5"], 176 help="Selects platform to synthesize circuit on. Default: %(default)s", 177 ) 178 179 parser.add_argument( 180 "-c", 181 "--config", 182 default="basic", 183 help="Select core configuration. " 184 + f"Available configurations: {', '.join(str_to_coreconfig.keys())}. Default: %(default)s", 185 ) 186 187 parser.add_argument( 188 "-u", 189 "--unit", 190 default="core", 191 help="Select core unit. " + f"Available units: {', '.join(core_units.keys())}. Default: %(default)s", 192 ) 193 194 parser.add_argument( 195 "-m", "--memory", default="xor-ilvt", choices=memory_types.keys(), help="Select superscalar memory type." 196 ) 197 198 parser.add_argument( 199 "--strip-debug", 200 action="store_true", 201 help="Remove debugging signals. Default: %(default)s", 202 ) 203 204 parser.add_argument( 205 "-v", 206 "--verbose", 207 action="store_true", 208 help="Enables verbose output. Default: %(default)s", 209 ) 210 211 args = parser.parse_args() 212 213 os.environ["AMARANTH_verbose"] = "true" if args.verbose else "false" 214 215 if args.config not in str_to_coreconfig: 216 raise KeyError(f"Unknown config '{args.config}'") 217 218 if args.unit not in core_units: 219 raise KeyError(f"Unknown core unit '{args.unit}'") 220 221 config = str_to_coreconfig[args.config] 222 if args.strip_debug: 223 config = config.replace(debug_signals=False) 224 225 config = config.replace(multiport_memory_type=memory_types[args.memory]) 226 227 synthesize(config, args.platform, core_units[args.unit]) 228 229 230 if __name__ == "__main__": 231 main()