1010from pprint import pformat
1111from typing import TYPE_CHECKING , List
1212
13- from amaranth import Module , Signal , ClockDomain , ClockSignal , ResetSignal
14-
15- from amaranth .lib import wiring , io
13+ from amaranth import Module , Signal , ClockDomain , ClockSignal , ResetSignal , unsigned
14+ from amaranth .lib import wiring , io , data
1615from amaranth .lib .cdc import FFSynchronizer
1716from amaranth .lib .wiring import Component , In , PureInterface
18-
1917from amaranth .back import rtlil #type: ignore[reportAttributeAccessIssue]
2018from amaranth .hdl import Fragment
2119from amaranth .hdl ._ir import PortDirection
@@ -91,10 +89,17 @@ def __init__(self,
9189 if self .direction in (io .Direction .Output , io .Direction .Bidir ):
9290 self ._o = Signal (self ._port_desc .width , name = f"{ self ._name } __o" )
9391 if self .direction is io .Direction .Bidir :
94- if "individual_oe" in self .iomodel and self .iomodel ["individual_oe" ]:
95- self ._oe = Signal (self ._port_desc .width , name = f"{ self ._name } __oe" , init = - 1 )
96- else :
92+ # the signals that get wired out to iocells. Always one per io.
93+ init_oe = - 1
94+ if 'init_oe' in port_desc .iomodel and port_desc .iomodel ['init_oe' ]:
95+ init_oe = port_desc .iomodel ['init_oe' ]
96+ self ._oes = Signal (self ._port_desc .width , name = f"{ self ._name } __oe" , init = init_oe )
97+ # the oe on the user side.
98+ if "individual_oe" not in self .iomodel or not self .iomodel ["individual_oe" ]:
9799 self ._oe = Signal (1 , name = f"{ self ._name } __oe" , init = - 1 )
100+ else :
101+ self ._oe = self ._oes
102+
98103 elif self .direction is io .Direction .Output :
99104 # Always create an _oe for output ports
100105 self ._oe = Signal (1 , name = f"{ self ._name } __oe" , init = - 1 )
@@ -103,11 +108,15 @@ def __init__(self,
103108
104109 def wire (self , m : Module , interface : PureInterface ):
105110 assert self .direction == interface .signature .direction #type: ignore
106- if hasattr (interface , 'i ' ):
111+ if hasattr (interface , '_i ' ):
107112 m .d .comb += interface .i .eq (self .i ) # type: ignore
108- for d in ['o ' , 'oe ' ]:
113+ for d in ['_o ' , '_oe ' ]:
109114 if hasattr (interface , d ):
110115 m .d .comb += getattr (self , d ).eq (getattr (interface , d ))
116+ if self ._oe is not None \
117+ and "individual_oe" in self .iomodel \
118+ and self .iomodel ["individual_oe" ]:
119+ m .d .comb += self ._oe .eq (self ._oes )
111120
112121 def instantiate_toplevel (self ):
113122 ports = []
@@ -250,6 +259,8 @@ def __init__(self,
250259 # Port Configuration
251260 # Input voltage trip level
252261 if self .direction in (io .Direction .Input , io .Direction .Bidir ):
262+ assert self ._i is not None
263+
253264 if 'trip_point' in port_desc .iomodel :
254265 trip_point = port_desc .iomodel ['trip_point' ]
255266 if trip_point not in __class__ ._VTrip_map :
@@ -258,21 +269,23 @@ def __init__(self,
258269 else :
259270 ib_mode_init = vtrip_init = 0
260271
261- self ._gpio_ib_mode_sel = Signal (1 , name = f"{ self ._name } $ib_mode_sel" , init = ib_mode_init )
262- self ._signals .append ((self ._gpio_ib_mode_sel , PortDirection .Output ))
263- self ._gpio_vtrip_sel = Signal (1 , name = f"{ self ._name } $vtrip_sel" , init = vtrip_init )
264- self ._signals .append ((self ._gpio_vtrip_sel , PortDirection .Output ))
272+ self ._ib_mode_sel = Signal (self . _i . shape (). width , name = f"{ self ._name } $ib_mode_sel" , init = ib_mode_init )
273+ self ._signals .append ((self ._ib_mode_sel , PortDirection .Output ))
274+ self ._vtrip_sel = Signal (self . _i . shape (). width , name = f"{ self ._name } $vtrip_sel" , init = vtrip_init )
275+ self ._signals .append ((self ._vtrip_sel , PortDirection .Output ))
265276
266277 # Drive mode
267278 if self .direction in (io .Direction .Output , io .Direction .Bidir ):
279+ if self ._o is None :
280+ raise ChipFlowError (f"Cannot set drive modes on a port with no outputs for { name } " )
268281 if 'drive_mode' in port_desc .iomodel :
269282 dm = Sky130DriveMode (port_desc .iomodel ['drive_mode' ])
270283 else :
271284 dm = Sky130DriveMode .STRONG_UP_STRONG_DOWN
272285 dm_init = __class__ ._DriveMode_map [dm ]
273- self . _gpio_dm = Signal ( 3 , name = f" { self ._name } $dm" , init = dm_init )
274- self ._signals . append (( self ._gpio_dm , PortDirection . Output ) )
275-
286+ dms_shape = data . ArrayLayout ( unsigned ( 3 ), self ._o . shape (). width )
287+ self ._dms = Signal ( dms_shape , name = f" { self ._name } $dm" , init = [ dm_init ] * self . _o . shape (). width )
288+ self . _signals . append (( self . _dms . as_value (), PortDirection . Output )) #type: ignore
276289 # Not enabled yet:
277290 self ._gpio_slow_sel = None # Select slew rate
278291 self ._gpio_holdover = None # Hold mode
@@ -295,6 +308,7 @@ def wire(self, m: Module, interface: PureInterface):
295308 def instantiate_toplevel (self ):
296309 ports = super ().instantiate_toplevel ()
297310 for s , d in self ._signals :
311+ logger .debug (f"Instantiating port for signal { repr (s )} " )
298312 logger .debug (f"Instantiating io${ s .name } top level port" )
299313 ports .append ((f"io${ s .name } " , s , d ))
300314 return ports
@@ -306,6 +320,15 @@ def ie(self):
306320 "input enable signal" )
307321 return self ._ie
308322
323+ @property
324+ def drive_mode (self ):
325+ if self ._dms is None :
326+ raise AttributeError ("SiliconPlatformPort with input direction does not have an "
327+ "input enable signal" )
328+ return self ._dms
329+
330+ #TODO: trip selection
331+
309332 def __invert__ (self ):
310333 result = Sky130Port (self ._name , self ._port_desc , invert = not self .invert )
311334 return result
0 commit comments