33import logging
44import os
55import sys
6+
7+ from dataclasses import dataclass
8+ from enum import StrEnum , auto
69from pathlib import Path
10+ from pprint import pformat
11+ from typing import List , Optional , TypedDict , Optional , Unpack , Union , Type
712
813from amaranth import Module , ClockDomain , ClockSignal , ResetSignal
9- from amaranth .lib import io
14+ from amaranth .lib import io , meta , wiring
15+ from amaranth .lib .wiring import In , Out
1016from amaranth .back import rtlil # type: ignore[reportAttributeAccessIssue]
17+ from amaranth .hdl import _ir , _ast
1118from amaranth .hdl ._ir import PortDirection
1219from amaranth .lib .cdc import FFSynchronizer
20+ from pydantic import BaseModel , ConfigDict
1321
14- from ._utils import load_pinlock
22+ from .. import ChipFlowError
23+ from .._signatures import I2CSignature , GPIOSignature , UARTSignature , SPISignature
24+ from ._utils import load_pinlock , _chipflow_schema_uri , amaranth_annotate , InputIOSignature , OutputIOSignature , BidirIOSignature
1525
1626
17- __all__ = ["SimPlatform" ]
1827logger = logging .getLogger (__name__ )
28+ __all__ = ["SimPlatform" , "BuildObject" , "BasicCxxBuild" ]
29+
30+
31+ class SimModelCapability (StrEnum ):
32+ LOAD_DATA = "load-data"
33+
34+
35+ @dataclass
36+ class SimModel :
37+ """
38+ Description of a model available from a BuildObject
39+ Attributes:
40+ name: the model name
41+ capabilities: List of capabilities of the model.
42+ signature: the wiring connection of the model. This is also used to match with interfaces.
43+ """
44+ name : str
45+ signature : Type [wiring .Signature ]
46+ capabilities : Optional [List [SimModelCapability ]] = None
47+
48+
49+ class BuildObject (BaseModel ):
50+ """
51+ Represents an object built from a compiled language
52+
53+ Attributes:
54+ capabilities: arbitary list of capability identifiers
55+ """
56+ kind : str = "base"
57+ models : List [SimModel ]
58+
59+ @classmethod
60+ def get_subclasses (cls ):
61+ return tuple (cls .__subclasses__ ())
62+
63+ def model_for_signature (self , signature : wiring .Signature ) -> SimModel | None :
64+ """
65+ Checks if this build object has a model for the given signature (matching by type equality)
66+
67+ Returns:
68+ A tuple of the model and a dict of parameters from the signature
69+ None on failure to find a matching model
70+ """
71+ for m in self .models :
72+ if isinstance (signature , m .signature ):
73+ return m
74+
75+
76+
77+ class BasicCxxBuild (BuildObject ):
78+ """
79+ Represents an object built from C++, where the compilation is simply done with a collection of
80+ cpp and hpp files, simply compiled and linked together with no dependencies
81+
82+ Assumes model name corresponds to the c++ class name and that the class constructors take
83+ a name followed by the wires of the interface.
84+
85+ Attributes:
86+ cpp_files: C++ files used to define the model
87+ hpp_files: C++ header files to define the model interfaces
88+ """
89+ kind : str = "basic-c++"
90+ cpp_files : List [Path ]
91+ hpp_files : Optional [List [Path ]] = None
92+ hpp_dirs : Optional [List [Path ]] = None
93+
94+
95+ _COMMON_MODELS = BasicCxxBuild (
96+ models = [
97+ SimModel ('spiflash_model' , SPISignature , [SimModelCapability .LOAD_DATA ]),
98+ SimModel ('uart_model' , UARTSignature ),
99+ SimModel ('i2c_model' , I2CSignature ),
100+ SimModel ('gpio_model' , GPIOSignature ),
101+ ],
102+ cpp_files = [ Path ('common' ,'sim' ,'models.cc' ) ],
103+ hpp_files = [ Path ('common' ,'sim' ,'models.h' ) ],
104+ )
19105
20106
21107class SimPlatform :
22-
23108 def __init__ (self , config ):
24109 self .build_dir = os .path .join (os .environ ['CHIPFLOW_ROOT' ], 'build' , 'sim' )
25110 self .extra_files = dict ()
26111 self .sim_boxes = dict ()
27112 self ._ports = {}
28113 self ._config = config
114+ self ._top_sim = {}
29115
30116 def add_file (self , filename , content ):
31117 if not isinstance (content , (str , bytes )):
@@ -67,6 +153,10 @@ def build(self, e):
67153 print ("read_rtlil sim_soc.il" , file = yosys_file )
68154 print ("hierarchy -top sim_top" , file = yosys_file )
69155 print ("write_cxxrtl -header sim_soc.cc" , file = yosys_file )
156+ main = Path (self .build_dir ) / "main.cc"
157+ with open (main , "w" ) as main_file :
158+ for p in self ._ports :
159+ print (p , file = main_file )
70160
71161 def instantiate_ports (self , m : Module ):
72162 if hasattr (self , "_pinlock" ):
@@ -76,10 +166,11 @@ def instantiate_ports(self, m: Module):
76166 for component , iface in pinlock .port_map .ports .items ():
77167 for k , v in iface .items ():
78168 for name , port_desc in v .items ():
79- logger .debug (f"Instantiating port { port_desc .port_name } : { port_desc } " )
80- invert = port_desc .invert if port_desc .invert else False
81- self ._ports [port_desc .port_name ] = io .SimulationPort (port_desc .direction , port_desc .width , invert = invert , name = port_desc .port_name )
82-
169+ logger .debug (f"Instantiating port { port_desc .port_name } : { port_desc } " )
170+ invert = port_desc .invert if port_desc .invert else False
171+ self ._ports [port_desc .port_name ] = io .SimulationPort (port_desc .direction , port_desc .width , invert = invert , name = port_desc .port_name )
172+ # TODO, allow user to add models
173+ #self._port_model = model_for_signature(port_desc.
83174 for clock in pinlock .port_map .get_clocks ():
84175 assert 'clock_domain' in clock .iomodel
85176 domain = clock .iomodel ['clock_domain' ]
@@ -123,8 +214,6 @@ def instantiate_ports(self, m: Module):
123214 "{OUTPUT_DIR}/sim_soc.cc" ,
124215 "{OUTPUT_DIR}/sim_soc.h" ,
125216 "{SOURCE_DIR}/main.cc" ,
126- "{COMMON_DIR}/models.cc" ,
127- "{COMMON_DIR}/models.h" ,
128217 "{COMMON_DIR}/vendor/nlohmann/json.hpp" ,
129218 "{COMMON_DIR}/vendor/cxxrtl/cxxrtl_server.h" ,
130219 ],
0 commit comments