Skip to content

Commit

Permalink
ci(precheck): experimental tapeout job
Browse files Browse the repository at this point in the history
generates the final .oas and publishes it as an artifact
  • Loading branch information
urish committed Nov 6, 2024
1 parent b9aeeba commit b2fc13f
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 1 deletion.
109 changes: 108 additions & 1 deletion .github/workflows/precheck.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: mpw_precheck
on:
workflow_dispatch:
workflow_run:
workflows: ["gds"]
workflows: ['gds']
types: [completed]

jobs:
Expand Down Expand Up @@ -71,3 +71,110 @@ jobs:
with:
name: precheck_results
path: tinytapeout-submission/precheck_results

mpw_tapeout_checks:
env:
USER_ID: 8086
PROJECT: caravel_openframe
KLAYOUT_VERSION: 0.29.2
MAGIC_VERSION: 8.3.471
PDK_ROOT: ${{ github.workspace }}/pdk
SKY130_PDK_VERSION: 0fe599b2afb6708d281543108caf8310912f54af
CARAVEL_ROOT: ${{ github.workspace }}/caravel

runs-on: ubuntu-22.04
steps:
- name: checkout repo
uses: actions/checkout@v4
with:
submodules: recursive

- name: Checkout Caravel
uses: actions/checkout@v4
with:
repository: efabless/caravel
path: ${{ env.CARAVEL_ROOT }}

- name: Install KLayout
run: |
wget https://www.klayout.org/downloads/Ubuntu-22/klayout_${KLAYOUT_VERSION}-1_amd64.deb
sudo apt-get install -y ./klayout_${KLAYOUT_VERSION}-1_amd64.deb
pip install klayout==${KLAYOUT_VERSION}
- name: Install magic VLSI
run: |
sudo apt-get install -y m4 python3 libx11-dev tcl-dev tk-dev libcairo2-dev mesa-common-dev libglu1-mesa-dev
git clone -b ${MAGIC_VERSION} https://github.com/RTimothyEdwards/magic magic
cd magic
./configure
make -j4
sudo make install
- name: Install xpath
run: sudo apt-get install -y libxml-xpath-perl

- name: Install Sky130 PDK
uses: TinyTapeout/volare-action@v1
with:
pdk_name: sky130
pdk_version: ${{ env.SKY130_PDK_VERSION }}
pdk_root: ${{ env.PDK_ROOT }}

- name: Download artifact (run id = ${{ github.event.workflow_run.id }})
uses: dawidd6/action-download-artifact@v3
id: download_artifact
with:
workflow: gds.yaml
run_id: ${{ github.event.workflow_run.id }}
workflow_conclusion: success
name: efabless_submission
path: tinytapeout-submission

- name: Create final .oas file
working-directory: ${{ env.CARAVEL_ROOT }}
run: |
sed -i 's/@cp -r /#@cp -r /' Makefile
cp ../tinytapeout-submission/gds/openframe_project_wrapper.gds gds
make openframe
make generate_fill
make final
strm2oas gds/caravel_$USER_ID.gds caravel_$USER_ID.oas
- name: FOM Density DRC
working-directory: ${{ env.CARAVEL_ROOT }}
env:
REPORT_FILE: klayout_fom_density_report.xml
run: |
wget https://raw.githubusercontent.com/efabless/foss-asic-tools/refs/heads/main/images/foss-asic-tools/addons/sak/klayout/tech/sky130A/fom_density.drc
klayout -b -r fom_density.drc -rd gds_input=gds/caravel_$USER_ID.gds -rd top_cell=caravel_$USER_ID -rd report_file=$REPORT_FILE
python ${{ github.workspace }}/sky130_drc/check_drc_report.py $REPORT_FILE
- name: Poly Density DRC
working-directory: ${{ env.CARAVEL_ROOT }}
env:
REPORT_FILE: klayout_poly_density_report.xml
run: |
klayout -b -r ${{ github.workflow}}/sky130_drc/poly_density.drc -rd gds_input=gds/caravel_$USER_ID.gds -rd top_cell=caravel_$USER_ID -rd report_file=$REPORT_FILE
python ${{ github.workspace }}/sky130_drc/check_drc_report.py $REPORT_FILE
- name: Publish final .gds file
uses: actions/upload-artifact@v4
if: always()
with:
name: final_gds
path: caravel/gds/caravel_*.gds

- name: Publish final .oas file
uses: actions/upload-artifact@v4
if: always()
with:
name: final_oas
path: caravel/caravel_*.oas

- name: Publish DRC reports
uses: actions/upload-artifact@v4
if: always()
with:
name: drc_reports
path: |
${{env.CARAVEL_ROOT}}/klayout_*_report.xml
12 changes: 12 additions & 0 deletions sky130_drc/check_drc_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import klayout.rdb as rdb
import sys

report_file = sys.argv[1]

report = rdb.ReportDatabase("DRC")
report.load(report_file)

if report.num_items() > 0:
raise RuntimeError(f"Klayout DRC failed with {report.num_items()} DRC violations")
else:
print("Klayout DRC passed")
159 changes: 159 additions & 0 deletions sky130_drc/poly_density.drc
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@

errs = 0
log("poly_density.drc:: sourcing design file=#{$gds_input} topcell=#{$top_cell} ...")
src = source($gds_input, $top_cell)
layout = src.layout
log("done.")


report("Density Checks", $report_file)

verbose(false)

diff_wildcard = "65/20"
tap_wildcard = "65/44"
polyfill_wildcard = "65,23/28"
polymk_wildcard = "23/0"

#$chip_boundary = input(235,4)
log("flattening chip boundary...")
$chip_boundary = input(235,4).flatten
log("done.")

$bbox = $chip_boundary.bbox

window_size = 700

if $step
step_size = $step.to_f
else
step_size = 70
end
log("step size = #{step_size}")

llx, lly, urx, ury = $bbox.left, $bbox.bottom, $bbox.right, $bbox.top
log("llx=#{llx} lly=#{lly} urx=#{urx} ury=#{ury}")
if urx-llx <= 0 || ury-lly <= 0
STDERR.puts "ERROR: poly_density.drc: fatal error, chip_boundary bbox empty or malformed"
errs += 1
end

cnt = 0
x_cnt = ((urx-step_size-llx) / step_size).ceil()
y_cnt = ((ury-step_size-lly) / step_size ).ceil()
tot = x_cnt * y_cnt
$poly_area_map = array = Array.new(x_cnt+1) { Array.new(y_cnt+1) { 0.0 } }
$tile_area_map = array = Array.new(x_cnt+1) { Array.new(y_cnt+1) { 0.0 } }

log("x_cnt = #{x_cnt}")
log("y_cnt = #{y_cnt}")

if x_cnt < 1 || y_cnt < 1
STDERR.puts "ERROR: poly_density.drc: fatal error, x-count or y-count < 1: #{x_cnt} #{y_cnt}"
errs += 1
end

# any errors till now?: abort.
if errs > 0
exit 1
end

class PolyArea < RBA::TileOutputReceiver
def put(ix, iy, tile, obj, dbu, clip)
if tile
poly_area_scaled = obj.to_f * dbu * dbu
#tile_area_scaled = tile.area * dbu * dbu
tile_area_scaled = (tile & ($bbox * (1.0 / dbu))).area * dbu * dbu
#puts "got area for tile #{ix+1},#{iy+1}: #{poly_area_scaled.to_s} #{tile_area_scaled.to_s}"
$poly_area_map[ix][iy] += poly_area_scaled
$tile_area_map[ix][iy] = tile_area_scaled
else
puts "empty tile #{ix+1},#{iy+1}"
end
end
def finish(success)
puts "finish received: success = #{success}"
end

end

tp = RBA::TilingProcessor::new

# register the custom receiver
tp.output("my_receiver", PolyArea::new)

tp.input("poly", layout.top_cell().begin_shapes_rec(layout.layer(66,20) ) )
tp.input("polyfill", layout.top_cell().begin_shapes_rec(layout.layer(28,28) ) )

tp.frame=$bbox
tp.tile_size(step_size, step_size) # 70x70 um tile size
log("dbu = #{layout.dbu}")
log("bbox_area = #{$bbox.area}")
tp.dbu = layout.dbu
tp.threads = $thr.to_i
# tp.scale_to_dbu = false

# The script clips the input at the tile and computes the (merged) area:
tp.queue("_output(my_receiver, _tile && ((poly | polyfill) & _tile).area)")

log("calculating subtile areas (= #{tot})...")
tp.execute("Job description")

cnt = 0
big_x_cnt = ((urx-window_size-llx) / step_size).ceil()
big_y_cnt = ((ury-window_size-lly) / step_size).ceil()
big_tot = big_x_cnt * big_y_cnt
tiles_per_step = (window_size / step_size).ceil()
log("tiles per step = #{tiles_per_step}")
log("calculating window step densities (= #{big_tot})...")
markers_min = polygon_layer
markers_max = polygon_layer
min_err = 0
max_err = 0
min_density = 1
max_density = 0
for x in (0 ... big_x_cnt)
log("{{ CHECK }} #{cnt}/#{big_tot.round}")
for y in (0 ... big_y_cnt)
poly_area = 0
tile_area = 0
poly_density = 0
#$poly_area_map.slice(x, tiles_per_step).each { |a| a.slice(y, tiles_per_step).each { |b| puts "#{x},#{y} = #{b}" } }
$poly_area_map.slice(x, tiles_per_step).each { |a| a.slice(y, tiles_per_step).each { |b| poly_area+=b } }
$tile_area_map.slice(x, tiles_per_step).each { |a| a.slice(y, tiles_per_step).each { |b| tile_area+=b } }
marker_box = box(x * step_size, y* step_size, (x+tiles_per_step)*step_size, (y+tiles_per_step)*step_size)
if tile_area > 0
poly_density = poly_area / tile_area
#log("poly density #{cnt}/#{big_tot.round} is #{poly_density} for area #{tile_area}")
if poly_density < min_density
min_density = poly_density
end
if poly_density > max_density
max_density = poly_density
end
if poly_density < 0.08
min_err += 1
markers_min.insert(marker_box)
log("poly density below 0.08 : #{cnt}/#{big_tot.round} is #{"%.4f" % poly_density} for tile (#{marker_box.to_s}), area = #{tile_area.round}")
elsif poly_density > 0.38
max_err += 1
markers_max.insert(marker_box)
log("poly density above 0.38 : #{cnt}/#{big_tot.round} is #{"%.4f" % poly_density} for tile (#{marker_box.to_s}), area = #{tile_area.round}")
end
end
cnt += 1
end
end

log("minimum poly density = #{"%.4f" % min_density}")
log("maximum poly density = #{"%.4f" % max_density}")

if min_err > 0
log("cpoly.pd.1d violations = #{min_err}")
markers_min.output("cpoly.pd.1d", "0.08 min poly pattern density")
end

if max_err > 0
log("cpoly.pd.1e violations = #{max_err}")
markers_max.output("cpoly.pd.1e", "0.38 max poly pattern density")
end

0 comments on commit b2fc13f

Please sign in to comment.