@@ -2762,56 +2762,33 @@ def maybe_abort_no_space(err, step_label):
27622762 if install_precheck :
27632763 step_num = 8 if not only_mode else ""
27642764 console .print (f"\n [bold]Step { step_num } :[/bold] Installing precheck..." )
2765- precheck_dir = Path .home () / 'mpw_precheck'
27662765
2767- # Check if already installed
2768- is_installed = precheck_dir .exists () and (precheck_dir / '.git' ).exists ()
2769-
2770- if is_installed and not overwrite :
2771- console .print ("[green]✓[/green] Precheck already installed" )
2772- elif dry_run :
2773- if is_installed :
2774- console .print ("[dim]Would reinstall mpw_precheck [--overwrite][/dim]" )
2775- else :
2776- console .print ("[dim]Would install mpw_precheck[/dim]" )
2766+ if dry_run :
2767+ console .print ("[dim]Would install/upgrade cf-precheck Python package[/dim]" )
27772768 else :
27782769 try :
2779- if precheck_dir .exists () and overwrite :
2780- console .print (f"[cyan]Removing existing { precheck_dir } ...[/cyan]" )
2781- shutil .rmtree (precheck_dir )
2782-
2783- if not precheck_dir .exists ():
2784- console .print ("[cyan]Cloning mpw_precheck...[/cyan]" )
2785- subprocess .run (
2786- ['git' , 'clone' , '--depth=1' , 'https://github.com/chipfoundry/mpw_precheck.git' , str (precheck_dir )],
2787- check = True ,
2788- capture_output = True ,
2789- text = True
2790- )
2791- console .print ("[green]✓[/green] Precheck cloned successfully" )
2792-
2793- if shutil .which ('docker' ) is None :
2794- had_errors = True
2795- console .print ("[red]✗[/red] Docker not found. Install Docker Desktop to pull the precheck image." )
2796- console .print ("[dim]Precheck repo cloned, but Docker image was not pulled.[/dim]" )
2797- else :
2798- console .print ("[cyan]Pulling precheck Docker image...[/cyan]" )
2799- subprocess .run (
2800- ['docker' , 'pull' , 'chipfoundry/mpw_precheck:latest' ],
2801- check = True ,
2802- capture_output = True
2770+ console .print ("[cyan]Installing cf-precheck...[/cyan]" )
2771+ subprocess .run (
2772+ [sys .executable , '-m' , 'pip' , 'install' , '--upgrade' , '-q' , 'cf-precheck' ],
2773+ check = True ,
2774+ capture_output = True ,
2775+ text = True ,
2776+ )
2777+ try :
2778+ result = subprocess .run (
2779+ [sys .executable , '-c' , 'from cf_precheck import __version__; print(__version__)' ],
2780+ capture_output = True , text = True ,
28032781 )
2804- console .print ("[green]✓[/green] Precheck Docker image ready" )
2805-
2782+ version = result .stdout .strip ()
2783+ console .print (f"[green]✓[/green] cf-precheck v{ version } installed" )
2784+ except Exception :
2785+ console .print ("[green]✓[/green] cf-precheck installed" )
28062786 except subprocess .CalledProcessError as e :
28072787 maybe_abort_no_space (e , "Precheck install" )
28082788 had_errors = True
2809- console .print (f"[red]✗[/red] Failed to install precheck: { e } " )
2789+ console .print (f"[red]✗[/red] Failed to install cf- precheck: { e } " )
28102790 if e .stderr :
28112791 console .print (f"[dim]{ e .stderr } [/dim]" )
2812- except OSError as e :
2813- had_errors = True
2814- console .print (f"[red]✗[/red] Failed to install precheck: { e } " )
28152792
28162793 # Summary
28172794 console .print ("\n " + "=" * 60 )
@@ -3216,21 +3193,22 @@ def repo_update(project_root, repo_owner, repo_name, branch, dry_run):
32163193
32173194@main .command ('precheck' )
32183195@click .option ('--project-root' , type = click .Path (exists = True , file_okay = False ), help = 'Path to the project directory (defaults to current directory)' )
3219- @click .option ('--disable-lvs' , is_flag = True , help = 'Disable LVS check and run specific checks only' )
3196+ @click .option ('--skip-checks' , multiple = True , help = 'Checks to skip (can be specified multiple times)' )
3197+ @click .option ('--magic-drc' , is_flag = True , help = 'Include Magic DRC check (optional, off by default)' )
32203198@click .option ('--checks' , multiple = True , help = 'Specific checks to run (can be specified multiple times)' )
32213199@click .option ('--dry-run' , is_flag = True , help = 'Show the command without running' )
3222- def precheck (project_root , disable_lvs , checks , dry_run ):
3223- """Run mpw_precheck validation on the project.
3200+ def precheck (project_root , skip_checks , magic_drc , checks , dry_run ):
3201+ """Run precheck validation on the project.
32243202
3225- This runs the MPW (Multi-Project Wafer) precheck tool to validate
3226- your design before submission.
3203+ This runs the cf- precheck tool to validate your design before
3204+ submission.
32273205
32283206 Examples:
3229- cf precheck # Run all checks
3230- cf precheck --disable-lvs # Skip LVS, run specific checks
3231- cf precheck --checks license --checks makefile # Run specific checks
3207+ cf precheck # Run all checks
3208+ cf precheck --skip-checks lvs # Skip LVS check
3209+ cf precheck --magic-drc # Include optional Magic DRC
3210+ cf precheck --checks topcell_check # Run specific checks only
32323211 """
3233- # If .cf/project.json exists in cwd, use it as default project_root
32343212 cwd_root , _ = get_project_json_from_cwd ()
32353213 if not project_root and cwd_root :
32363214 project_root = cwd_root
@@ -3239,20 +3217,17 @@ def precheck(project_root, disable_lvs, checks, dry_run):
32393217
32403218 project_root_path = Path (project_root )
32413219
3242- # Check if project is initialized (allow graceful return)
32433220 if not check_project_initialized (project_root_path , 'precheck' , dry_run = dry_run , allow_graceful = True ):
32443221 console .print (f"[red]✗[/red] Project not initialized. Please run 'cf init' first." )
32453222 console .print ("[yellow]Dependencies are required before running precheck.[/yellow]" )
32463223 return
32473224
32483225 project_json_path = project_root_path / '.cf' / 'project.json'
32493226
3250- # Check project type - GPIO config not needed for openframe
32513227 with open (project_json_path , 'r' ) as f :
32523228 project_data = json .load (f )
32533229 project_type = project_data .get ('project' , {}).get ('type' , 'digital' )
32543230
3255- # Check if GPIO configuration exists (not needed for openframe)
32563231 if project_type != 'openframe' :
32573232 gpio_config = get_gpio_config_from_project_json (str (project_json_path ))
32583233 if not gpio_config or len (gpio_config ) == 0 :
@@ -3261,10 +3236,8 @@ def precheck(project_root, disable_lvs, checks, dry_run):
32613236 console .print ("[cyan]Please run 'cf gpio-config' to configure GPIO settings first.[/cyan]" )
32623237 raise click .Abort ()
32633238
3264- precheck_root = Path .home () / 'mpw_precheck'
32653239 pdk_root = project_root_path / 'dependencies' / 'pdks'
32663240
3267- # Detect PDK from project.json
32683241 pdk = 'sky130A'
32693242 if project_json_path .exists ():
32703243 try :
@@ -3274,124 +3247,98 @@ def precheck(project_root, disable_lvs, checks, dry_run):
32743247 except :
32753248 pass
32763249
3277- # Check if precheck is installed
3278- if not precheck_root .exists ():
3279- console .print (f"[red]✗[/red] mpw_precheck not found at { precheck_root } " )
3280- console .print ("[yellow]Run 'cf setup --only-precheck' to install[/yellow]" )
3281- return
3282-
3283- # Check if PDK exists
32843250 if not (pdk_root / pdk ).exists ():
32853251 console .print (f"[red]✗[/red] PDK not found at { pdk_root / pdk } " )
32863252 console .print ("[yellow]Run 'cf setup --only-pdk' to install[/yellow]" )
32873253 return
32883254
3289- # Check Docker availability
3290- docker_available = shutil .which ('docker' ) is not None
3291- if not docker_available :
3255+ if shutil .which ('docker' ) is None :
32923256 console .print ("[red]✗[/red] Docker not found. Docker is required to run precheck." )
32933257 return
32943258
3295- # Build the checks list
3296- if checks :
3297- # User specified custom checks
3298- checks_list = list (checks )
3299- elif disable_lvs :
3300- # Default checks when LVS is disabled
3301- checks_list = [
3302- 'license' , 'makefile' , 'default' , 'documentation' , 'consistency' ,
3303- 'gpio_defines' , 'xor' , 'magic_drc' , 'klayout_feol' , 'klayout_beol' ,
3304- 'klayout_offgrid' , 'klayout_met_min_ca_density' ,
3305- 'klayout_pin_label_purposes_overlapping_drawing' , 'klayout_zeroarea'
3306- ]
3307- else :
3308- # All checks (default behavior)
3309- checks_list = []
3310-
3311- # Display configuration
3312- console .print ("\n " + "=" * 60 )
3313- console .print ("[bold cyan]MPW Precheck[/bold cyan]" )
3314- console .print (f"Project: [yellow]{ project_root_path } [/yellow]" )
3315- console .print (f"PDK: [yellow]{ pdk } [/yellow]" )
3316- if disable_lvs :
3317- console .print ("Mode: [yellow]LVS disabled[/yellow]" )
3318- if checks_list :
3319- console .print (f"Checks: [yellow]{ ', ' .join (checks_list )} [/yellow]" )
3320- else :
3321- console .print ("Checks: [yellow]All checks[/yellow]" )
3322- console .print ("=" * 60 + "\n " )
3323-
3324- # Build Docker command
3325- import getpass
3326- import pwd
3327-
3328- user_id = os .getuid ()
3329- group_id = os .getgid ()
3330-
33313259 pdk_path = pdk_root / pdk
3332- pdkpath = pdk_path # Same as PDK_PATH in the Makefile
3333- ipm_dir = Path .home () / '.ipm'
33343260
3335- # Create .ipm directory if it doesn't exist
3336- if not ipm_dir .exists ():
3337- ipm_dir .mkdir (parents = True , exist_ok = True )
3261+ docker_image = 'chipfoundry/mpw_precheck:latest'
33383262
33393263 docker_cmd = [
33403264 'docker' , 'run' , '--rm' ,
3341- '-v' , f'{ precheck_root } :{ precheck_root } ' ,
33423265 '-v' , f'{ project_root_path } :{ project_root_path } ' ,
33433266 '-v' , f'{ pdk_root } :{ pdk_root } ' ,
3344- '-v' , f'{ ipm_dir } :{ ipm_dir } ' ,
3345- '-e' , f'INPUT_DIRECTORY={ project_root_path } ' ,
3346- '-e' , f'PDK_PATH={ pdk_path } ' ,
3347- '-e' , f'PDK_ROOT={ pdk_root } ' ,
3348- '-e' , f'PDKPATH={ pdkpath } ' ,
3349- '-u' , f'{ user_id } :{ group_id } ' ,
3350- 'chipfoundry/mpw_precheck:latest' ,
3351- 'bash' , '-c' ,
3267+ docker_image ,
3268+ 'cf-precheck' ,
3269+ '-i' , str (project_root_path ),
3270+ '-p' , str (pdk_path ),
3271+ '-c' , '/opt/caravel' ,
33523272 ]
33533273
3354- # Build the precheck command
3355- precheck_cmd = f'cd { precheck_root } ; python3 mpw_precheck.py --input_directory { project_root_path } --pdk_path { pdk_path } '
3274+ if magic_drc :
3275+ docker_cmd .append ('--magic-drc' )
3276+
3277+ if skip_checks :
3278+ docker_cmd .extend (['--skip-checks' ] + list (skip_checks ))
33563279
3357- if checks_list :
3358- precheck_cmd += ' ' + ' ' . join ( checks_list )
3280+ if checks :
3281+ docker_cmd . extend ( list ( checks ) )
33593282
3360- docker_cmd .append (precheck_cmd )
3283+ checks_display = ', ' .join (checks ) if checks else 'All checks'
3284+ console .print ("\n " + "=" * 60 )
3285+ console .print ("[bold cyan]CF Precheck[/bold cyan]" )
3286+ console .print (f"Project: [yellow]{ project_root_path } [/yellow]" )
3287+ console .print (f"PDK: [yellow]{ pdk } [/yellow]" )
3288+ if skip_checks :
3289+ console .print (f"Skipping: [yellow]{ ', ' .join (skip_checks )} [/yellow]" )
3290+ if magic_drc :
3291+ console .print ("Magic DRC: [yellow]enabled[/yellow]" )
3292+ console .print (f"Checks: [yellow]{ checks_display } [/yellow]" )
3293+ console .print ("=" * 60 + "\n " )
33613294
33623295 if dry_run :
33633296 console .print ("[bold yellow]Dry run - would execute:[/bold yellow]\n " )
33643297 console .print ("[dim]" + ' ' .join (docker_cmd ) + "[/dim]" )
33653298 return
33663299
3367- # Run precheck
3368- console .print ("[cyan]Running mpw_precheck...[/cyan]" )
3300+ # Pull/update Docker image before running
3301+ console .print (f"[cyan]Checking for Docker image updates...[/cyan]" )
3302+ try :
3303+ subprocess .run (
3304+ ['docker' , 'pull' , docker_image ],
3305+ check = True ,
3306+ capture_output = True ,
3307+ )
3308+ console .print (f"[green]✓[/green] Docker image up to date" )
3309+ except subprocess .CalledProcessError :
3310+ # Image might already be available locally, warn but continue
3311+ result = subprocess .run (
3312+ ['docker' , 'image' , 'inspect' , docker_image ],
3313+ capture_output = True ,
3314+ )
3315+ if result .returncode != 0 :
3316+ console .print (f"[red]✗[/red] Docker image '{ docker_image } ' not found. Run 'cf setup --only-precheck' or check your connection." )
3317+ return
3318+ console .print ("[yellow]⚠[/yellow] Could not check for image updates (using cached image)" )
3319+
3320+ console .print ("[cyan]Running cf-precheck...[/cyan]\n " )
33693321
33703322 try :
3371- # Use Popen for better signal handling
33723323 process = subprocess .Popen (
33733324 docker_cmd ,
3374- cwd = str (precheck_root ),
33753325 preexec_fn = os .setsid if os .name != 'nt' else None
33763326 )
33773327
3378- # Wait for process to complete
33793328 returncode = process .wait ()
33803329
3381- console .print ("" ) # Add newline
3330+ console .print ("" )
33823331 if returncode == 0 :
33833332 console .print ("[green]✓[/green] Precheck passed!" )
3384- elif returncode == - 2 or returncode == 130 : # SIGINT
3333+ elif returncode == - 2 or returncode == 130 :
33853334 console .print ("[yellow]⚠[/yellow] Precheck interrupted by user" )
33863335 sys .exit (130 )
33873336 else :
3388- console .print (f"[red]✗[/red] Precheck failed with exit code { returncode } " )
3389- console .print (f"[yellow]Check the output above for details[/yellow]" )
3337+ console .print (f"[red]✗[/red] Precheck failed (exit code { returncode } )" )
33903338 sys .exit (returncode )
33913339
33923340 except KeyboardInterrupt :
33933341 console .print ("\n [yellow]⚠[/yellow] Precheck interrupted by user" )
3394- # Try to stop the Docker container gracefully
33953342 try :
33963343 if os .name != 'nt' :
33973344 os .killpg (os .getpgid (process .pid ), signal .SIGTERM )
0 commit comments