Merge pull request #41 from lip6/wip-gf180mcu

Wip gf180mcu
This commit is contained in:
Jean-Paul Chaput 2023-09-26 00:30:11 +02:00 committed by GitHub
commit db0adbcc02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 591 additions and 92 deletions

View File

@ -690,10 +690,10 @@ namespace Anabatic {
bool Vertex::isH () const
{
GCell* gcell = getGCell();
if (gcell->isDevice()) return isiHorizontal();
else if ((gcell->isHChannel())||(gcell->isHRail())) return true;
else if (gcell->isStrut()| gcell->isMatrix() ) return ((gcell->getWidth() > gcell->getHeight())||(gcell->getWidth() == gcell->getHeight()));
else return false;
if ( gcell->isDevice()) return isiHorizontal();
else if ((gcell->isHChannel()) or (gcell->isHRail())) return true;
else if ( gcell->isStrut() or gcell->isMatrix()) return ((gcell->getWidth() > gcell->getHeight())||(gcell->getWidth() == gcell->getHeight()));
else return false;
}

View File

@ -181,7 +181,7 @@ def _routing ():
cfg.katana.globalRipupLimit = 5
cfg.katana.globalRipupLimit = [1, None]
cfg.katana.longGlobalRipupLimit = 5
cfg.chip.padCoreSide = 'South'
cfg.chip.padCoreSide = 'North'
# Plugins setup
cfg.clockTree.minimumSide = u(5.04) * 6
cfg.clockTree.buffer = 'gf180mcu_fd_sc_mcu9t5v0__clkbuf_2'

View File

@ -99,6 +99,10 @@ def _routing():
)
af.addCellGauge(cg)
af.setCellGauge('StdCell3V3Lib')
lg5 = af.getRoutingGauge('StdCell3V3Lib').getLayerGauge( 5 )
lg5.setType( CRL.RoutingLayerGauge.PowerSupply )
env = af.getEnvironment()
env.setRegister( '.*sff.*' )
# Place & Route setup
with CfgCache(priority=Cfg.Parameter.Priority.ConfigurationFile) as cfg:
@ -162,17 +166,8 @@ def _routing():
cfg.katana.globalRipupLimit = 5
cfg.katana.globalRipupLimit = [1, None]
cfg.katana.longGlobalRipupLimit = 5
cfg.chip.padCoreSide = 'South'
# Plugins setup
with CfgCache(priority=Cfg.Parameter.Priority.ConfigurationFile) as cfg:
cfg.viewer.minimumSize = 500
cfg.viewer.pixelThreshold = 10
cfg.chip.block.rails.count = 5
cfg.chip.block.rails.hWidth = u(2.68)
cfg.chip.block.rails.vWidth = u(2.68)
cfg.chip.block.rails.hSpacing = u(0.7)
cfg.chip.block.rails.vSpacing = u(0.7)
cfg.clockTree.minimumSide = l(600)
cfg.clockTree.buffer = 'buf_x1'
cfg.clockTree.placerEngine = 'Etesian'

View File

@ -27,8 +27,9 @@ def _routing ():
cfg.chip.block.rails.vWidth = u(30.0)
cfg.chip.block.rails.hSpacing = u( 6.0)
cfg.chip.block.rails.vSpacing = u( 6.0)
cfg.chip.padCorner = 'gf180mcu_fd_io__cor_5lm'
cfg.chip.padSpacers = 'gf180mcu_fd_io__fill10_5lm,gf180mcu_fd_io__fill5_5lm,gf180mcu_fd_io__fill1_5lm'
#cfg.chip.padCorner = 'gf180mcu_fd_io__cor'
#cfg.chip.padSpacers = 'gf180mcu_fd_io__fill10,gf180mcu_fd_io__fill5,gf180mcu_fd_io__fill1'
cfg.chip.padCoreSide = 'North'
af = AllianceFramework.get()
cg = CellGauge.create( 'LEF.GF_IO_Site'
, 'Metal2' # pin layer name.
@ -57,7 +58,6 @@ def _loadIoLib ( pdkDir ):
print( ' o Setup GF180MCU I/O library in {}.'.format( ioLib.getName() ))
io.vprint( 1, ' o Setup GF180MCU I/O library in {}.'.format( ioLib.getName() ))
cellsDir = pdkDir / 'libraries' / 'gf180mcu_fd_io' / 'latest' / 'cells'
print( cellsDir )
for lefFile in cellsDir.glob( '*/*_5lm.lef' ):
print( lefFile )
gdsFile = lefFile.with_suffix( '.gds' )
@ -65,6 +65,12 @@ def _loadIoLib ( pdkDir ):
Gds.setTopCellName( gdsFile.stem[:-4] )
Gds.load( ioLib, gdsFile.as_posix(), Gds.Layer_0_IsBoundary|Gds.NoBlockages )
LefImport.load( lefFile.as_posix() )
# Demote the VDD/VSS nets until we understand how that works.
for cell in ioLib.getCells():
for net in cell.getNets():
if net.getName() in ('VDD', 'VSS'):
net.setExternal( False )
net.setGlobal( False )
af.wrapLibrary( ioLib, 1 )

View File

@ -17,8 +17,8 @@
${CMAKE_CURRENT_SOURCE_DIR}/designflow/dreal.py
${CMAKE_CURRENT_SOURCE_DIR}/designflow/sv2v.py
${CMAKE_CURRENT_SOURCE_DIR}/designflow/svase.py
${CMAKE_CURRENT_SOURCE_DIR}/designflow/surelog.py
${CMAKE_CURRENT_SOURCE_DIR}/designflow/yosys.py
${CMAKE_CURRENT_SOURCE_DIR}/designflow/yosysnp.py
${CMAKE_CURRENT_SOURCE_DIR}/designflow/blif2vst.py
${CMAKE_CURRENT_SOURCE_DIR}/designflow/pnr.py
${CMAKE_CURRENT_SOURCE_DIR}/designflow/klayout.py
@ -65,6 +65,7 @@
${CMAKE_CURRENT_SOURCE_DIR}/plugins/core2chip/niolib.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/core2chip/libresocio.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/core2chip/sky130.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/core2chip/gf180mcu.py
)
set ( pyPluginChip ${CMAKE_CURRENT_SOURCE_DIR}/plugins/chip/__init__.py
${CMAKE_CURRENT_SOURCE_DIR}/plugins/chip/constants.py

View File

@ -1,4 +1,5 @@
import shutil
from pathlib import Path
from doit.exceptions import TaskFailed
from .task import FlowTask
@ -37,6 +38,9 @@ class Clean ( FlowTask ):
if filePath.is_file():
print( ' - {:<40} [removed]'.format( filePath.as_posix() ))
filePath.unlink()
elif filePath.is_dir():
print( ' - {:<40} [removed (directory)]'.format( filePath.as_posix() ))
shutil.rmtree( filePath )
else:
print( ' - {}'.format( filePath.as_posix() ))
if doExtrasClean and len(self.extrasGlobs):

View File

@ -24,7 +24,7 @@ class PnR ( FlowTask ):
textMode = True
@staticmethod
def mkRule ( rule, targets=[], depends=[], script=None ):
def mkRule ( rule, targets=[], depends=[], script=None, topName=None ):
"""
Creates a new rule instance (``doit`` task).
@ -36,11 +36,12 @@ class PnR ( FlowTask ):
:param script: A callable, typically a ``scriptMain()`` function. The only
requirement is that it should accept one keyworded argument (``**kw``).
"""
return PnR( rule, targets, depends, script )
return PnR( rule, targets, depends, script, topName )
def __init__ ( self, rule, targets, depends, script ):
def __init__ ( self, rule, targets, depends, script, topName ):
super().__init__( rule, targets, depends )
self.script = script
self.topName = topName
self.addClean( self.targets )
def __repr__ ( self ):
@ -70,8 +71,11 @@ class PnR ( FlowTask ):
if self.script and not callable(self.script):
e = ErrorMessage( 1, 'PnR.doTask(): "script" argument is *not* callable.' )
return TaskFailed( e )
kw = {}
if self.script:
self.script( **{} )
if self.topName:
kw[ 'loadCell' ] = self.topName
self.script( **kw )
if not PnR.textMode:
# Run in graphic mode.
ha = Viewer.HApplication.create( [] )

View File

@ -0,0 +1,70 @@
import os.path
import shutil
import subprocess
from pathlib import Path
from doit.exceptions import TaskFailed
from .task import FlowTask
def printCommand ( command ):
commandBin = command[0]
print( commandBin, command[1] )
for arg in command[2:]:
print( ' '*len(commandBin), arg )
class Surelog ( FlowTask ):
@staticmethod
def mkRule ( rule, depends, top, incdirs=[], libdirs=[], defines=[], options=[], flags=0 ):
return Surelog( rule, depends, top, incdirs, libdirs, defines, options, flags )
def __init__ ( self, rule, depends, top, incdirs, libdirs, defines, options, flags ):
self.flags = flags
self.top = top
self.incdirs = incdirs
self.libdirs = libdirs
self.defines = defines
self.options = options
self.success = True
targets = FlowTask._normFileList( [ self.top + '.uhdm' ] )
depends = FlowTask._normFileList( depends )
super().__init__( rule, targets, depends )
self.addClean( targets )
def __repr__ ( self ):
return '<surelog {} top={}>'.format( self.main, self.top )
@property
def main ( self ):
return self.file_depend( 0 )
def doTask ( self ):
from ..helpers.io import ErrorMessage
for incdir in self.incdirs:
if not Path(incdir).is_dir():
e = ErrorMessage( 1, [ 'Surelog.doTask(): Include directory not found "{}"'
, '"{}"'.format( incdir ) ] )
return TaskFailed( e )
command = [ 'surelog', '-parse' ]
command += [ ' '.join( self.options ) ]
command += [ '-D{}'.format(d) for d in self.defines ]
command += [ '-I{}'.format(i) for i in self.incdirs ]
for libdir in self.libdirs:
command += [ '-L', libdir ]
command += [ '-top', self.top ]
command += [ depend.as_posix() for depend in self.depends ]
printCommand( command )
status = subprocess.call( command )
if status != 0: return False
shutil.move( 'slpp_all/surelog.uhdm', self.file_target(0) )
return True
def create_doit_tasks ( self ):
return { 'basename' : self.basename
, 'actions' : [ self.doTask ]
, 'doc' : 'Run {}.'.format( self )
, 'file_dep' : self.file_dep
, 'targets' : self.targets
}

View File

@ -2,7 +2,6 @@
import os.path
import subprocess
from pathlib import Path
from pyosys import libyosys as yosys
from doit.exceptions import TaskFailed
from .task import FlowTask
@ -19,11 +18,12 @@ class Sv2v ( FlowTask ):
FlagLog = 0x00000001
@staticmethod
def mkRule ( rule, targets, depends, top=None, incdirs=[], libdirs=[], defines=[], flags=0 ):
def mkRule ( rule, targets, depends, top, incdirs=[], libdirs=[], defines=[], flags=0 ):
return Sv2v( rule, targets, depends, top, incdirs, libdirs, defines, flags )
def __init__ ( self, rule, targets, depends, top, incdirs, libdirs, defines, flags ):
self.flags = flags
self.top = top
self.incdirs = incdirs
self.libdirs = libdirs
self.defines = defines
@ -31,10 +31,6 @@ class Sv2v ( FlowTask ):
self.success = True
targets = FlowTask._normFileList( targets )
depends = FlowTask._normFileList( depends )
if top is not None:
self.top = top
else:
self.top = depends[0].stem
if targets == []:
targets.append( self.top + '.v' )
#if self.flags & Sv2v.FlagLog:

View File

@ -2,7 +2,6 @@
import os.path
import subprocess
from pathlib import Path
from pyosys import libyosys as yosys
from doit.exceptions import TaskFailed
from .task import FlowTask
@ -19,11 +18,12 @@ class Svase ( FlowTask ):
FlagLog = 0x00000001
@staticmethod
def mkRule ( rule, targets, depends, top=None, incdirs=[], libdirs=[], defines=[], svargs=[], flags=0 ):
def mkRule ( rule, targets, depends, top, incdirs=[], libdirs=[], defines=[], svargs=[], flags=0 ):
return Svase( rule, targets, depends, top, incdirs, libdirs, defines, svargs, flags )
def __init__ ( self, rule, targets, depends, top, incdirs, libdirs, defines, svargs, flags ):
self.flags = flags
self.top = top
self.svargs = svargs
self.incdirs = incdirs
self.libdirs = libdirs
@ -32,15 +32,12 @@ class Svase ( FlowTask ):
self.success = True
targets = FlowTask._normFileList( targets )
depends = FlowTask._normFileList( depends )
if top is not None:
self.top = top
else:
self.top = depends[0].stem
if targets == []:
targets.append( self.top + '.v' )
#if self.flags & Svase.FlagLog:
# self.log = Path( self.top + '.log' )
# targets.append( self.log )
targets.append( './slang-args.txt' )
super().__init__( rule, targets, depends )
self.addClean( targets )

View File

@ -284,7 +284,6 @@ def setupGf180mcu_c4m ( checkToolkit=None
cfg.misc.verboseLevel2 = True
cfg.etesian.graphics = 3
cfg.etesian.spaceMargin = 0.10
cfg.anabatic.topRoutingLayer = 'metal6'
cfg.katana.eventsLimit = 4000000
af = CRL.AllianceFramework.get()
lg5 = af.getRoutingGauge('StdCell3V3Lib').getLayerGauge( 5 )

View File

@ -1,16 +1,27 @@
import os.path
import shutil
import subprocess
from pathlib import Path
from doit.exceptions import TaskFailed
from .task import FlowTask
usePyYosys = True
try:
from pyosys import libyosys as yosys
except:
usePyYosys = False
class BadLiberty ( Exception ): pass
class Yosys ( FlowTask ):
_liberty = None
FlagLog = 0x00000001
FlagQuiet = 0x00000002
FlagSystemVerilog = 0x00000004
_liberty = None
@staticmethod
def setLiberty ( liberty ):
@ -25,20 +36,58 @@ class Yosys ( FlowTask ):
Yosys._liberty = liberty
@staticmethod
def mkRule ( rule, depends, top=None, blackboxes=[], flattens=[] ):
return Yosys( rule, depends, top, blackboxes, flattens )
def mkRule ( rule
, depends
, top =None
, blackboxes=[]
, flattens =[]
, svOptions =None
, svDefines =None
, svIncdirs =None
, svLibdirs =None
, script =[]
, flags =0 ):
return Yosys( rule
, depends
, top
, blackboxes
, flattens
, svOptions
, svDefines
, svIncdirs
, svLibdirs
, script
, flags )
def __init__ ( self, rule, depends, top, blackboxes, flattens ):
def __init__ ( self, rule
, depends
, top
, blackboxes
, flattens
, svOptions
, svDefines
, svIncdirs
, svLibdirs
, script
, flags ):
super().__init__( rule, [], depends )
self.success = True
self.blackboxes = blackboxes
self.flattens = flattens
self.depends += blackboxes
self.svOptions = svOptions
self.svDefines = svDefines
self.svIncdirs = svIncdirs
self.svLibdirs = svLibdirs
self.flags = flags
self.script = script
self.success = True
if top is not None:
self.top = top
else:
self.top = self.main.stem
self.targets = [ Path( self.top + '.blif') ]
if not usePyYosys:
self.targets.append( Path( self.top + '.ys' ))
self.addClean( self.targets )
def __repr__ ( self ):
@ -53,11 +102,6 @@ class Yosys ( FlowTask ):
def main ( self ):
return self.file_depend( 0 )
def _run_pass ( self, command ):
from pyosys import libyosys as yosys
if self.success is not True: return
yosys.run_pass( command, self.tool )
def _loadDesign ( self, design ):
from ..helpers.io import ErrorMessage
if self.success is not True: return
@ -66,13 +110,49 @@ class Yosys ( FlowTask ):
self.success = TaskFailed( e )
return
design = Path( design )
if design.suffix == '.v' : self._run_pass( 'read_verilog -sv {}'.format( design.as_posix() ))
elif design.suffix == '.il': self._run_pass( 'read_ilang {}'.format( design.as_posix() ))
if design.suffix == '.v' :
self.script.append( 'read_verilog -sv {}'.format( design.as_posix() ))
elif design.suffix == '.il' : self.script.append( 'read_ilang {}'.format( design.as_posix() ))
elif design.suffix == '.uhdm':
self.script.append( 'plugin -i systemverilog' )
self.script.append( 'read_uhdm {}'.format( design.as_posix() ))
else:
e = ErrorMessage( 1, 'Yosys._loadDesign(): Unsupported input format for "{}".'.format( design ))
self.success = TaskFailed( e )
return
def _loadSVDesign ( self ):
from ..helpers.io import ErrorMessage
if self.success is not True: return
self.script.append( 'plugin -i systemverilog' )
svFileArgs = ''
for svFile in self.depends:
if not isinstance(svFile,Path):
svFile = Path( svFile )
if not svFile.is_file():
print( '[WARNING] Can\'t find SV file "{}".'.format( svFile.as_posix() ))
continue
svFileArgs += ' {}'.format( svFile.as_posix() )
defineArgs = ''
for define in self.svDefines:
defineArgs += ' -D{}'.format( define )
includeArgs = ''
for incdir in self.svIncdirs:
includeArgs += ' -I{}'.format( incdir )
libArgs = ''
for libdir in self.svLibdirs:
libArgs += ' -L {}'.format( libdir )
options = ' '.join( self.svOptions )
scriptArgs = { 'options' :options
, 'defines' :defineArgs
, 'includes' :includeArgs
, 'libraries':libArgs
, 'svFiles' :svFileArgs
, 'top' : self.top }
self.script.append( 'read_systemverilog {options} -top {top} {defines} {includes} {libraries} {svFiles}' \
.format( **scriptArgs ))
def _loadBlackboxes ( self ):
if self.success is not True: return
for blackbox in self.blackboxes:
@ -81,11 +161,34 @@ class Yosys ( FlowTask ):
def _doFlattens ( self ):
if self.success is not True: return
flattens = ' '.join( self.flattens )
self._run_pass( 'flatten {}\n'.format( flattens ))
self._run_pass( 'hierarchy -top {}\n'.format( self.top ))
self.script.append( 'flatten {}'.format( flattens ))
self.script.append( 'hierarchy -top {}'.format( self.top ))
def _runScript ( self ):
from ..helpers.io import ErrorMessage
if usePyYosys:
tool = yosys.Design()
for command in self.script:
yosys.run_pass( command, tool )
if shutil.which( 'yosys' ): command = [ 'yosys' ]
elif shutil.which( 'yowasp-yosys' ): command = [ 'yowasp-yosys' ]
else:
e = ErrorMessage( 1, [ 'Yosys._runScript(): Neither "yosys" nor "yowasp-yosys" has been found' ] )
self.success = TaskFailed( e )
return
ysFile = self.targets[-1].as_posix()
with open( ysFile, 'w' ) as ysFd:
ysFd.write( '\n'.join( self.script ).format( liberty =str(self.liberty)
, cellname=self.main.stem
, top =self.top ))
if self.flags & Yosys.FlagQuiet: command += [ '-q' ]
if self.flags & Yosys.FlagLog: command += [ '-l', self.log.as_posix() ]
command += [ '-s', ysFile ]
status = subprocess.call( command )
self.success = (status == 0)
def doTask ( self ):
from pyosys import libyosys as yosys
from ..helpers.io import ErrorMessage
if self.liberty is None:
e = ErrorMessage( 1, [ 'Yosys.doTask(): "liberty" has not been set' ] )
@ -94,18 +197,21 @@ class Yosys ( FlowTask ):
e = ErrorMessage( 1, [ 'Yosys.doTask(): File not found "{}"'
, '"{}"'.format( self.liberty.as_posix() ) ] )
return TaskFailed( e )
#print( 'Yosys.doTask() on "{}"'.format( self.design ))
self.tool = yosys.Design()
#print( 'Yosys.doTask() on "{}"'.format( self.main ))
self._loadBlackboxes()
self._loadDesign( self.main )
self._run_pass( 'hierarchy -check -top {}'.format( self.top ))
self._run_pass( 'synth -top {}'.format( self.top ))
if self.flags & Yosys.FlagSystemVerilog:
self._loadSVDesign()
else:
self._loadDesign( self.main )
self.script.append( 'hierarchy -check -top {}'.format( self.top ))
self.script.append( 'synth -top {}'.format( self.top ))
self._doFlattens()
self._run_pass( 'memory' )
self._run_pass( 'dfflibmap -liberty {}'.format( self.liberty.as_posix() ))
self._run_pass( 'abc -liberty {}'.format( self.liberty.as_posix() ))
self._run_pass( 'clean' )
self._run_pass( 'write_blif {}'.format( self.targets[0] ))
self.script.append( 'memory' )
self.script.append( 'dfflibmap -liberty {}'.format( self.liberty.as_posix() ))
self.script.append( 'abc -liberty {}'.format( self.liberty.as_posix() ))
self.script.append( 'clean')
self.script.append( 'write_blif {}'.format( self.targets[0] ))
self._runScript()
return self.success
def create_doit_tasks ( self ):

View File

@ -2,7 +2,6 @@
import os.path
import subprocess
from pathlib import Path
from pyosys import libyosys as yosys
from doit.exceptions import TaskFailed
from .task import FlowTask

View File

@ -484,6 +484,19 @@ class Block ( object ):
trace( 550, '\tCORE AB is {}\n'.format(self.conf.cell.getAbutmentBox()) )
if self.conf.isCoreBlock:
self.conf.setupICore()
minHCorona = self.conf.minHCorona
minVCorona = self.conf.minVCorona
innerBb = Box( self.conf.coreAb )
innerBb.inflate( minHCorona, minVCorona )
coronaAb = self.conf.corona.getAbutmentBox()
if innerBb.getWidth() > coronaAb.getWidth():
raise ErrorMessage( 1, 'Core is too wide to fit into the corona, needs {} but only has {}.' \
.format( DbU.getValueString(innerBb .getWidth())
, DbU.getValueString(coronaAb.getWidth()) ) )
if innerBb.getHeight() > coronaAb.getHeight():
raise ErrorMessage( 1, 'Core is too tall to fit into the corona, needs {} but only has {}.' \
.format( DbU.getValueString(innerBb .getHeight())
, DbU.getValueString(coronaAb.getHeight()) ) )
self.conf.setRoutingBb( self.conf.cellPnR.getAbutmentBox() )
def flattenNets ( self ):

View File

@ -15,6 +15,7 @@
import sys
import re
import os.path
import collections
from operator import itemgetter
from ... import Cfg
from ...Hurricane import DataBase, Breakpoint, DbU, Box, Transformation, \
@ -1449,7 +1450,22 @@ class BlockConf ( GaugeConf ):
for ioPinSpec in self.ioPinsArg:
self.ioPins.append( IoPin( *ioPinSpec ) )
for line in range(len(self.ioPadsArg)):
self.chipConf.addIoPad( self.ioPadsArg[line], line )
bits = []
if not isinstance(self.ioPadsArg[line][-1],str) \
and isinstance(self.ioPadsArg[line][-1],collections.Iterable):
bits = self.ioPadsArg[line][-1]
elif isinstance(self.ioPadsArg[line][-1],int):
bits = range( self.ioPadsArg[line][-1] )
if bits != []:
for bit in bits:
spec = [ self.ioPadsArg[line][0]
, self.ioPadsArg[line][1]
]
for i in range( 2, len(self.ioPadsArg[line])-1 ):
spec.append( self.ioPadsArg[line][i].format( bit ))
self.chipConf.addIoPad( spec, line )
else:
self.chipConf.addIoPad( self.ioPadsArg[line], line )
trace( 550, ',-' )
@property

View File

@ -99,11 +99,14 @@ class Chip ( Block ):
trace( 550, '\tminHCorona={}\n'.format(DbU.getValueString( minHCorona )))
trace( 550, '\tminVCorona={}\n'.format(DbU.getValueString( minVCorona )))
else:
print( ' - Using harness.' )
self.padsCorona = harnessPads.Corona( self )
self.padsCorona.doLayout()
innerBb = Box( self.conf.coreAb )
innerBb.inflate( minHCorona, minVCorona )
coronaAb = self.conf.corona.getAbutmentBox()
trace( 550, '\tinnerBb:{}\n'.format(innerBb) )
trace( 550, '\tcoronaAb:{}\n'.format(coronaAb) )
if innerBb.getWidth() > coronaAb.getWidth():
raise ErrorMessage( 1, 'Core is too wide to fit into the corona, needs {} but only has {}.' \
.format( DbU.getValueString(innerBb .getWidth())

View File

@ -693,11 +693,11 @@ class Corona ( object ):
if plug.getMasterNet().isGlobal():
net = self.conf.cell.getNet( plug.getMasterNet().getName() )
if not net:
raise ErrorMessage( 1, 'Corona._padAnalysis(): Ring net "%s" is not connected and there is no global net (in pad \"%s").' \
% plug.getMasterNet().getName(), padCell.getName() )
raise ErrorMessage( 1, 'Corona._padAnalysis(): Ring net "{}" is not connected and there is no global net (in pad "{}").' \
.format( plug.getMasterNet().getName(), padCell.getName() ))
else:
raise ErrorMessage( 1, 'Corona._padAnalysis(): Ring net "%s" is neither connected nor global (in pad \"%s").' \
% plug.getMasterNet().getName(), padCell.getName() )
raise ErrorMessage( 1, 'Corona._padAnalysis(): Ring net "{}" is neither connected nor global (in pad "{}").' \
.format( plug.getMasterNet().getName(), padCell.getName() ))
if net:
self.padRails.append( ( net
, component.getLayer()

View File

@ -375,6 +375,7 @@ class IoPad ( object ):
or self.nets[0].chipExtNetName.startswith('io_in') \
or self.nets[0].chipExtNetName.startswith('io_out')
if hasEnable:
trace( 550, '\tself.nets = {}\n'.format( self.nets ))
if len(self.nets) < 2:
enableNet = self.coreToChip.newEnableForNet( self.nets[0] )
self.nets.append( self.coreToChip.getIoNet( enableNet ) )
@ -387,6 +388,7 @@ class IoPad ( object ):
connexions.append( ( self.nets[0].chipIntNet , padInfo.inputNet ) )
connexions.append( ( self.coreToChip.newDummyNet(), padInfo.outputNet ) )
if hasEnable:
trace( 550, '\tenable Pad={} <-> {}\n'.format( padInfo.enableNet, self.nets[1].chipIntNet ))
connexions.append( ( self.nets[1].chipIntNet, padInfo.enableNet ) )
elif (self.direction == IoPad.TRI_OUT) and (len(self.nets) < 2):
self.nets[0].setFlags( IoNet.DoExtNet )
@ -415,6 +417,11 @@ class IoPad ( object ):
connexions.append( ( self.nets[0].chipIntNet, padInfo.inputNet ) )
connexions.append( ( self.nets[1].chipIntNet, padInfo.outputNet ) )
connexions.append( ( self.nets[2].chipIntNet, padInfo.enableNet ) )
for controlInfo in padInfo.controlNets:
controlNet = self.coreToChip.newControlForPad( self.ioPadConf, controlInfo )
self.nets.append( self.coreToChip.getIoNet( controlNet ) )
self.nets[-1].buildNets()
connexions.append( ( self.nets[-1].chipIntNet, controlInfo.name ) )
if not self.coreToChip.useHarness():
self.pads.append( Instance.create( self.coreToChip.chip
, self.padInstanceName
@ -448,13 +455,21 @@ class CoreToChip ( object ):
to the core actually bearing information.
"""
class IoControlInfo ( object ):
def __init__ ( self, name, defaultState ):
self.name = name
self.defaultState = defaultState
pass
class IoPadInfo ( object ):
def __init__ ( self, flags, padName, padNet, coreNets ):
self.flags = flags
self.name = padName
self.padNet = padNet
self.coreNets = coreNets
def __init__ ( self, flags, padName, padNet, coreNets, controlNets=[] ):
self.flags = flags
self.name = padName
self.padNet = padNet
self.coreNets = coreNets
self.controlNets = [ CoreToChip.IoControlInfo( net[0], net[1] ) for net in controlNets ]
return
@property
@ -508,6 +523,10 @@ class CoreToChip ( object ):
if not masterNetO: masterNet = instance.getMasterCell().getNet( chipNet.getName() )
elif isinstance(masterNetO,Net): masterNet = masterNetO
else: masterNet = instance.getMasterCell().getNet( masterNetO )
if not masterNet:
raise ErrorMessage( 1, [ 'CoreToChip._connect(): No net "{}" in cell "{}".' \
.format( masterNetO, instance.getMasterCell().getName() )
] )
instance.getPlug( masterNet ).setNet( chipNet )
return
@ -571,6 +590,22 @@ class CoreToChip ( object ):
self.dummyNetCount += 1
return dummy
def newControlNet ( self, controlName, constantType ):
"""
Create a new control signal, in *core* cell, to control the associated I/O pad.
The control signal is tied to a constant value, either zero or one.
:param controlName: The name of the control net *in the core cell*.
:param constantType: Whether the control signal is set to zero or one.
"""
instance = self.conf.constantsConf.createInstance( self.core, constantType )
control = Net.create( self.core, controlName )
control.setExternal ( True )
control.setDirection( Net.Direction.OUT )
getPlugByName( instance, self.conf.constantsConf.output(constantType) ).setNet( control )
self.conf.addClonedCell( self.conf.core )
return control
def newEnableForNet ( self, ioNet ):
"""
Create a new enable signal, in *core* cell, to control the associated I/O pad.
@ -582,13 +617,16 @@ class CoreToChip ( object ):
else:
raise ErrorMessage( 2, 'CoreToChip.newEnableForNet(): Net "{}" is neither IN nor OUT.' \
.format(ioNet.coreNet.getName()) )
instance = self.conf.constantsConf.createInstance( self.core, constantType )
enable = Net.create( self.core, ioNet.enableNetName )
enable.setExternal ( True )
enable.setDirection( Net.Direction.OUT )
getPlugByName( instance, self.conf.constantsConf.output(constantType) ).setNet( enable )
self.conf.addClonedCell( self.conf.core )
return enable
return self.newControlNet( ioNet.enableNetName, constantType )
def newControlForPad ( self, ioPadInfo, ioControlInfo ):
"""
Create a new control signal, in *core* cell, to control the associated I/O pad.
This is to be used for all I/O pads controls nets, save the "enable" signal.
"""
constantType = ConstantsConf.ONE if ioControlInfo.defaultState else ConstantsConf.ZERO
controlNetName = '{}_{}'.format( ioPadInfo.instanceName, ioControlInfo.name )
return self.newControlNet( controlNetName, constantType )
def getIoNet ( self, coreNet ):
"""

View File

@ -0,0 +1,248 @@
# -*- coding: utf-8 -*-
#
# This file is part of the Coriolis Software.
# Copyright (c) Sorbonne Université 2020-2023, All Rights Reserved
#
# +-----------------------------------------------------------------+
# | C O R I O L I S |
# | C u m u l u s - P y t h o n T o o l s |
# | |
# | Author : Jean-Paul CHAPUT |
# | E-mail : Jean-Paul.Chaput@lip6.fr |
# | =============================================================== |
# | Python : "./plugins/core2chip/libresocio.py" |
# +-----------------------------------------------------------------+
"""
Core2Chip configuration for the Global Foudries 180nm I/O pad library (GF180MCU).
"""
import sys
import re
from ...Hurricane import DbU, DataBase, UpdateSession, Breakpoint, \
Transformation , Instance , Net
from ...CRL import Catalog, AllianceFramework
from ...helpers import trace
from ...helpers.io import ErrorMessage, WarningMessage
from ...helpers.overlay import CfgCache
from .core2chip import CoreToChip as BaseCoreToChip, IoNet, IoPad
class CoreToChip ( BaseCoreToChip ):
"""
Provide pad-specific part for GF180MCU I/O pads (works in real mode).
"""
rePadType = re.compile(r'(?P<type>.+)_(?P<index>[\d]+)$')
def __init__ ( self, core ):
with CfgCache() as cfg:
cfg.chip.useAbstractPads = False
self.ioPadNames = { 'in' :'gf180mcu_fd_io__in_s'
, 'bidir' :'gf180mcu_fd_io__bi_t'
, 'analog' :'gf180mcu_fd_io__asig_5p0'
, 'vdd' :'gf180mcu_fd_io__dvdd'
, 'vss' :'gf180mcu_fd_io__dvss'
, 'corner' :'gf180mcu_fd_io__cor'
, 'spacer1' :'gf180mcu_fd_io__fill1'
, 'spacer5' :'gf180mcu_fd_io__fill5'
, 'spacer10' :'gf180mcu_fd_io__fill10'
}
BaseCoreToChip.__init__ ( self, core )
self.ringNetNames = { 'DVDD' : None
, 'DVSS' : None
#, 'VDD' : None
#, 'VSS' : None
}
self.ioPadInfos = [ BaseCoreToChip.IoPadInfo( IoPad.IN
, self.ioPadNames['in']
, 'PAD', ['Y'], [ ( 'PU' , False )
, ( 'PD' , False )
] )
, BaseCoreToChip.IoPadInfo( IoPad.BIDIR
, self.ioPadNames['bidir']
, 'PAD', ['A', 'Y', 'OE'], [ ( 'SL' , True )
, ( 'CS' , True )
, ( 'PU' , False )
, ( 'PD' , False )
, ( 'PDRV0', False )
, ( 'PDRV1', False )
, ( 'IE' , True )
] )
, BaseCoreToChip.IoPadInfo( IoPad.ANALOG
, self.ioPadNames['analog']
, 'ASIG5V', ['asig5v'] )
, BaseCoreToChip.IoPadInfo( IoPad.CORNER
, self.ioPadNames['corner']
, None, [] )
, BaseCoreToChip.IoPadInfo( IoPad.FILLER
, self.ioPadNames['spacer1']
, None, [] )
, BaseCoreToChip.IoPadInfo( IoPad.FILLER
, self.ioPadNames['spacer5']
, None, [] )
, BaseCoreToChip.IoPadInfo( IoPad.FILLER
, self.ioPadNames['spacer10']
, None, [] )
]
self.cornerCount = 0
self.spacerCount = 0
self.padSpacers = []
self._getPadLib()
return
def _getPadLib ( self ):
"""
Check that the I/O pad library is present and pre-load the spacer cells.
"""
def _cmpPad ( pad ):
"""Used to sort I/O pads by decreasing width."""
return pad.getAbutmentBox().getWidth()
self.padLib = AllianceFramework.get().getLibrary( "iolib" )
if not self.padLib:
message = [ 'CoreToChip.libresocio._getPadLib(): Unable to find Alliance "iolib" library' ]
raise ErrorMessage( 1, message )
for ioPadInfo in self.ioPadInfos:
if ioPadInfo.flags & IoPad.FILLER:
spacerCell = self.padLib.getCell( ioPadInfo.name )
if spacerCell: self.padSpacers.append( spacerCell )
else:
raise ErrorMessage( 1, 'CoreToChip.gf180mcu._getPadLib(): Missing spacer cell "{}"'.format(spacerName) )
self.padSpacers = sorted( self.padSpacers, key=_cmpPad, reverse=True )
def getNetType ( self, netName ):
if netName.lower().startswith('vss') or netName.lower().startswith('dvss'): return Net.Type.GROUND
if netName.lower().startswith('vdd') or netName.lower().startswith('dvdd'): return Net.Type.POWER
return Net.Type.LOGICAL
def isGlobal ( self, netName ):
if netName in self.ringNetNames: return True
return False
def getCell ( self, masterCellName ):
#cell = self.padLib.getCell( masterCellName )
cell = AllianceFramework.get().getCell( masterCellName, Catalog.State.Views )
if not cell:
raise ErrorMessage( 1, 'libresocio.getCell(): I/O pad library "%s" does not contain cell named "%s"' \
% (self.padLib.getName(),masterCellName) )
return cell
def _buildAllGroundPads ( self, ioPadConf ):
coreNet = self.core .getNet( ioPadConf.coreSupplyNetName )
coronaNet = self.corona.getNet( ioPadConf.coreSupplyNetName )
chipNet = self.chip .getNet( ioPadConf.coreSupplyNetName )
padNet = self.chip .getNet( ioPadConf.padSupplyNetName )
if not coronaNet:
coronaNet = Net.create( self.corona, ioPadConf.coreSupplyNetName )
coronaNet.setExternal( True )
coronaNet.setGlobal ( True )
coronaNet.setType ( Net.Type.GROUND )
self.icore.getPlug( coreNet ).setNet( coronaNet )
if not chipNet:
chipNet = Net.create( self.chip, ioPadConf.coreSupplyNetName )
chipNet.setExternal( True )
chipNet.setType ( Net.Type.GROUND )
if not padNet:
padNet = Net.create( self.chip, ioPadConf.padSupplyNetName )
padNet.setExternal( True )
padNet.setType ( Net.Type.GROUND )
coronaPlug = self.icorona.getPlug( coronaNet )
if not coronaPlug.getNet():
coronaPlug.setNet( chipNet )
self.ringNetNames['DVSS' ] = chipNet
#self.ringNetNames['VSS' ] = padNet
ioPadConf.pads.append( Instance.create( self.chip
, 'p_iovss_{}'.format(ioPadConf.index)
, self.getCell(self.ioPadNames['vss']) ) )
#self._connect( ioPadConf.pads[0], chipNet, 'VSS' )
self._connect( ioPadConf.pads[0], padNet , 'DVSS' )
self.groundPadCount += 1
self.chipPads += ioPadConf.pads
def _buildAllPowerPads ( self, ioPadConf ):
trace( 550, ',+', '\tgf180mcu.CoreToChip()\n' )
trace( 550, '\tcoreSupplyNetName="{}"\n'.format( ioPadConf.coreSupplyNetName ))
trace( 550, '\tpadSupplyNetName ="{}"\n'.format( ioPadConf.padSupplyNetName ))
coreNet = self.core .getNet( ioPadConf.coreSupplyNetName )
coronaNet = self.corona.getNet( ioPadConf.coreSupplyNetName )
chipNet = self.chip .getNet( ioPadConf.coreSupplyNetName )
padNet = self.chip .getNet( ioPadConf.padSupplyNetName )
if not coronaNet:
coronaNet = Net.create( self.corona, ioPadConf.coreSupplyNetName )
coronaNet.setExternal( True )
coronaNet.setGlobal ( True )
coronaNet.setType ( Net.Type.POWER )
self.icore.getPlug( coreNet ).setNet( coronaNet )
if not chipNet:
chipNet = Net.create( self.chip, ioPadConf.coreSupplyNetName )
chipNet.setExternal( True )
chipNet.setType ( Net.Type.POWER )
self.icorona.getPlug( coronaNet ).setNet( chipNet )
trace( 550, '\tchipNet ="{}"\n'.format( chipNet ))
if not padNet:
padNet = Net.create( self.chip, ioPadConf.padSupplyNetName )
padNet.setExternal( True )
padNet.setType ( Net.Type.POWER )
self.ringNetNames['DVDD'] = chipNet
#self.ringNetNames['VDD'] = padNet
trace( 550, '\tpadNet ="{}"\n'.format( padNet ))
ioPadConf.pads.append( Instance.create( self.chip
, 'p_iovdd_{}'.format(ioPadConf.index)
, self.getCell(self.ioPadNames['vdd']) ) )
#self._connect( ioPadConf.pads[0], chipNet, 'VDD' )
self._connect( ioPadConf.pads[0], padNet , 'DVDD' )
self.powerPadCount += 1
self.chipPads += ioPadConf.pads
trace( 550, '-,' )
def _buildClockPads ( self, ioPadConf ):
"""For "GF180MCU" there is no specialized clock I/O pad. So do nothing."""
pass
def _connectClocks ( self ):
"""For "GF180MCU" there is no pad internal clock ring. So do nothing."""
pass
def hasCornerCell ( self ):
"""Overload of CoreToChip, YES we have dedicated corner cells."""
return True
def hasFillerCells ( self ):
"""Overload of CoreToChip, YES we have dedicated filler cells."""
return True
def getCornerCell ( self ):
"""Return the model of corner cell."""
return self.getCell( self.ioPadNames['corner'] )
def createSpacer ( self, gapWidth ):
"""Return a new instance of spacer cell."""
spacerCell = None
for candidate in self.padSpacers:
if gapWidth >= candidate.getAbutmentBox().getWidth():
spacerCell = candidate
break
if not spacerCell:
return None
spacer = Instance.create( self.chip
, 'pad_spacer_{}'.format( self.spacerCount )
, spacerCell )
self.spacerCount += 1
#self._connect( spacer, self.ringNetNames['vddring'], 'vddring' )
self._connect( spacer, self.ringNetNames['DVDD'], 'DVDD' )
#self._connect( spacer, self.ringNetNames['gndring'], 'gndring' )
self._connect( spacer, self.ringNetNames['DVSS'], 'DVSS' )
return spacer
def createCorner ( self, instanceName=None ):
"""Return a new instance of corner cell."""
if instanceName is None:
instanceName = 'pad_corner_{}'.format( self.cornerCount )
corner = Instance.create( self.chip, instanceName, self.getCornerCell() )
self.cornerCount += 1
self._connect( corner, self.ringNetNames['DVDD'], 'DVDD' )
#self._connect( corner, self.ringNetNames['vddcore'], 'vddcore' )
self._connect( corner, self.ringNetNames['DVSS'], 'DVSS' )
#self._connect( corner, self.ringNetNames['gndcore'], 'gndcore' )
return corner

View File

@ -120,7 +120,7 @@ Contact::Contact(Net* net, const Layer* layer, DbU::Unit x, DbU::Unit y, DbU::Un
throw Error("Contact::Contact(): Can't create " + _TName("Contact") + ", NULL layer.");
const BasicLayer* basicLayer = dynamic_cast<const BasicLayer*>( layer );
if (not basicLayer or basicLayer->getMaterial() != BasicLayer::Material::cut) return;
if (not basicLayer or (basicLayer->getMaterial() != BasicLayer::Material::cut)) return;
if ( _width < _layer->getMinimalSize() ) _width = _layer->getMinimalSize();
if ( _height < _layer->getMinimalSize() ) _height = _layer->getMinimalSize();
}
@ -150,7 +150,7 @@ Contact::Contact(Net* net, Component* anchor, const Layer* layer, DbU::Unit dx,
_anchorHook.attach(anchor->getBodyHook());
const BasicLayer* basicLayer = dynamic_cast<const BasicLayer*>( layer );
if (not basicLayer or basicLayer->getMaterial() != BasicLayer::Material::cut) return;
if (not basicLayer or (basicLayer->getMaterial() != BasicLayer::Material::cut)) return;
if ( _width < _layer->getMinimalSize() ) _width = _layer->getMinimalSize();
if ( _height < _layer->getMinimalSize() ) _height = _layer->getMinimalSize();
}
@ -180,29 +180,33 @@ Contact* Contact::create(Component* anchor, const Layer* layer, DbU::Unit dx, Db
// *************************
{
DbU::Unit twoGrid = DbU::fromGrid( 2 );
DbU::Unit minSize = _layer->getMinimalSize();
bool rvalue = true;
if (_layer->isSymbolic()) {
if (not _width ) _width = _layer->getMinimalSize();
if (not _height) _height = _layer->getMinimalSize();
if (not _width ) _width = minSize;
if (not _height) _height = minSize;
} else {
if ((_width) and _checkMinSize and (_width < _layer->getMinimalSize())) {
const BasicLayer* basicLayer = dynamic_cast<const BasicLayer*>( _layer );
if (not basicLayer or basicLayer->getMaterial() != BasicLayer::Material::cut)
minSize = 0;
if ((_width) and _checkMinSize and (_width < minSize)) {
cerr << Warning( "Contact::_postCheck(): Width %s is inferior to layer minimal size %s, bumping.\n"
" (on %s)"
, DbU::getValueString(_width).c_str()
, DbU::getValueString(_layer->getMinimalSize()).c_str()
, DbU::getValueString(minSize).c_str()
, getString(this).c_str() )
<< endl;
_width = _layer->getMinimalSize();
_width = minSize;
rvalue = false;
}
if ((_height) and _checkMinSize and (_height < _layer->getMinimalSize())) {
if ((_height) and _checkMinSize and (_height < minSize)) {
cerr << Warning( "Contact::_postCheck(): Height %s is inferior to layer minimal size %s, bumping.\n"
" (on %s)"
, DbU::getValueString(_height).c_str()
, DbU::getValueString(_layer->getMinimalSize()).c_str()
, DbU::getValueString(minSize).c_str()
, getString(this).c_str() )
<< endl;
_height = _layer->getMinimalSize();
_height = minSize;
rvalue = false;
}
if ((_width % twoGrid) and _checkMinSize) {

View File

@ -223,8 +223,8 @@ namespace Hurricane {
// );
// Rotate the painter for the cartouche if in A4+Landscape mode.
if ( (_printer->paperSize () == QPrinter::A4)
and (_printer->orientation() == QPrinter::Landscape) ) {
if ( (_printer->pageLayout().pageSize().id() == QPageSize::A4)
and (_printer->pageLayout().orientation() == QPageLayout::Landscape) ) {
painter.translate ( _paperWidth - frameMargin(), frameMargin() );
painter.rotate ( -90 );
} else
@ -326,7 +326,7 @@ namespace Hurricane {
_printer = printer;
_printer->setResolution ( _dpi );
_printer->setPageMargins( 0.0, 0.0, 0.0, 0.0, QPrinter::DevicePixel );
_printer->setPageMargins( QMarginsF(0.0, 0.0, 0.0, 0.0), QPageLayout::Millimeter );
_paperWidth = _printer->width ();
_paperHeight = _printer->height ();
@ -336,7 +336,7 @@ namespace Hurricane {
_ypaper = (imageOnly) ? 0 : frameMargin();
// Substract the cartouche size only for A4 format.
if ( _printer->orientation() == QPrinter::Landscape ) {
if ( _printer->pageLayout().orientation() == QPageLayout::Landscape ) {
_drawingWidth -= cartoucheHeight();
} else {
_drawingHeight -= cartoucheHeight();