| # WARNING: this is somewhat paramaterized, but is only tested on A50T/A35T with the traditional ROI |
| # Your ROI should at least have a SLICEL on the left |
| |
| # Number of package inputs going to ROI |
| set DIN_N 8 |
| if { [info exists ::env(DIN_N) ] } { |
| set DIN_N "$::env(DIN_N)" |
| } |
| # Number of ROI outputs going to package |
| set DOUT_N 8 |
| if { [info exists ::env(DOUT_N) ] } { |
| set DOUT_N "$::env(DOUT_N)" |
| } |
| # How many rows between pins |
| # Reduces routing pressure |
| set PITCH 1 |
| if { [info exists ::env(PITCH) ] } { |
| set PITCH "$::env(PITCH)" |
| } |
| |
| if { [info exists ::env(URAY_ROI_HCLK)] } { |
| set URAY_ROI_HCLK "$::env(URAY_ROI_HCLK)" |
| } else { |
| puts "WARNING: No HCLK has been set" |
| } |
| |
| # Setting all the PIPs for DIN and DOUT |
| if { [info exists ::env(URAY_ROI_DIN_LPIP)] } { |
| set DIN_LPIP "$::env(URAY_ROI_DIN_LPIP)" |
| } else { puts "Warning: No left pip for DIN has been set" } |
| |
| if { [info exists ::env(URAY_ROI_DIN_RPIP)] } { |
| set DIN_RPIP "$::env(URAY_ROI_DIN_RPIP)" |
| } else { puts "Warning: No right pip for DIN has been set" } |
| |
| if { [info exists ::env(URAY_ROI_DOUT_LPIP)] } { |
| set DOUT_LPIP "$::env(URAY_ROI_DOUT_LPIP)" |
| } else { puts "Warning: No left pip for DOUT has been set" } |
| |
| if { [info exists ::env(URAY_ROI_DOUT_RPIP)] } { |
| set DOUT_RPIP "$::env(URAY_ROI_DOUT_RPIP)" |
| } else { puts "Warning: No right pip for DOUT has been set" } |
| |
| # Setting all INT_L/R tiles for DIN and DOUT X values |
| if { [info exists ::env(URAY_ROI_DIN_INT_L_X)] } { |
| set DIN_INT_L_X "$::env(URAY_ROI_DIN_INT_L_X)" |
| } else { puts "Warning: No INT_L for DIN has been set" } |
| |
| if { [info exists ::env(URAY_ROI_DIN_INT_R_X)] } { |
| set DIN_INT_R_X "$::env(URAY_ROI_DIN_INT_R_X)" |
| } else { puts "Warning: No INT_R for DIN has been set" } |
| |
| if { [info exists ::env(URAY_ROI_DOUT_INT_L_X)] } { |
| set DOUT_INT_L_X "$::env(URAY_ROI_DOUT_INT_L_X)" |
| } else { puts "Warning: No INT_L for DOUT has been set" } |
| |
| if { [info exists ::env(URAY_ROI_DOUT_INT_R_X)] } { |
| set DOUT_INT_R_X "$::env(URAY_ROI_DOUT_INT_R_X)" |
| } else { puts "Warning: No INT_R for DOUT has been set" } |
| |
| # X12 in the ROI, X10 just to the left |
| # Start at bottom left of ROI and work up |
| # (IOs are to left) |
| # SLICE_X12Y100:SLICE_X27Y149 |
| # set X_BASE 12 |
| set URAY_ROI_X0 [lindex [split [lindex [split "$::env(URAY_ROI)" Y] 0] X] 1] |
| set URAY_ROI_X1 [lindex [split [lindex [split "$::env(URAY_ROI)" X] 2] Y] 0] |
| set URAY_ROI_Y0 [lindex [split [lindex [split "$::env(URAY_ROI)" Y] 1] :] 0] |
| set URAY_ROI_Y1 [lindex [split "$::env(URAY_ROI)" Y] 2] |
| |
| set X_BASE $URAY_ROI_X0 |
| set Y_BASE $URAY_ROI_Y0 |
| |
| set Y_CLK_BASE $Y_BASE |
| # Clock lut in middle |
| set Y_DIN_BASE [expr "$Y_CLK_BASE + $PITCH"] |
| # Sequential |
| # set Y_DOUT_BASE [expr "$Y_DIN_BASE + $DIN_N"] |
| # At top. This relieves routing pressure by spreading things out |
| # Note: can actually go up one more if we want |
| set Y_DOUT_BASE [expr "$URAY_ROI_Y1 - $DOUT_N * $PITCH"] |
| |
| # Y_OFFSET: offset amount to shift the components on the y column to avoid hard blocks |
| set Y_OFFSET 24 |
| |
| set part "$::env(URAY_PART)" |
| set pincfg "" |
| if { [info exists ::env(URAY_PINCFG) ] } { |
| set pincfg "$::env(URAY_PINCFG)" |
| } |
| set roiv "../roi_base.v" |
| if { [info exists ::env(URAY_ROIV) ] } { |
| set roiv "$::env(URAY_ROIV)" |
| } |
| set roiv_trim [string map {.v v} $roiv] |
| |
| puts "Environment" |
| puts " URAY_ROI: $::env(URAY_ROI)" |
| puts " X_BASE: $X_BASE" |
| puts " Y_DIN_BASE: $Y_DIN_BASE" |
| puts " Y_CLK_BASE: $Y_CLK_BASE" |
| puts " Y_DOUT_BASE: $Y_DOUT_BASE" |
| |
| source ../../../utils/utils.tcl |
| |
| create_project -force -part $::env(URAY_PART) design design |
| read_verilog ../top.v |
| read_verilog $roiv |
| set fixed_xdc "" |
| if { [info exists ::env(URAY_FIXED_XDC) ] } { |
| set fixed_xdc "$::env(URAY_FIXED_XDC)" |
| } |
| |
| # added flatten_hierarchy |
| # dout_shr was getting folded into the pblock |
| # synth_design -top top -flatten_hierarchy none -no_lc -keep_equivalent_registers -resource_sharing off |
| synth_design -top top -flatten_hierarchy none -verilog_define DIN_N=$DIN_N -verilog_define DOUT_N=$DOUT_N |
| |
| if {$fixed_xdc ne ""} { |
| read_xdc $fixed_xdc |
| } |
| |
| # Map of top level net names to IOB pin names |
| array set net2pin [list] |
| |
| if {$part eq "xczu7ev-ffvc1156-2-i"} { |
| if {$pincfg eq "ZYNQUSP-SWBUT"} { |
| set sw_but "E4 D4" ;# DIP_SW_0 DIP_SW_1 |
| set leds "D5 D6" ;# LED0 LED1 |
| |
| # 125 MHz CLK onboard |
| set clkp "F23" |
| set clkn "E23" |
| set net2pin(clkp) $clkp |
| set net2pin(clk) $clkn |
| |
| # DIN |
| for {set i 0} {$i < $DIN_N} {incr i} { |
| set pin [lindex $sw_but $i] |
| set net2pin(din[$i]) $pin |
| } |
| |
| # DOUT |
| for {set i 0} {$i < $DOUT_N} {incr i} { |
| set pin [lindex $leds $i] |
| set net2pin(dout[$i]) $pin |
| } |
| |
| } else { |
| error "Unsupported config $pincfg" |
| } |
| } else { |
| error "Pins: unsupported part $part" |
| } |
| |
| # Now actually apply the pin definitions |
| puts "Applying pin definitions" |
| foreach {net pin} [array get net2pin] { |
| puts " Net $net to pin $pin" |
| set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS33" [get_ports $net] |
| } |
| |
| if {$fixed_xdc eq ""} { |
| create_pblock roi |
| set_property EXCLUDE_PLACEMENT 1 [get_pblocks roi] |
| set_property CONTAIN_ROUTING true [get_pblocks roi] |
| set_property DONT_TOUCH true [get_cells roi] |
| add_cells_to_pblock [get_pblocks roi] [get_cells roi] |
| resize_pblock [get_pblocks roi] -add "$::env(URAY_ROI)" |
| |
| #set_property CFGBVS VCCO [current_design] |
| set_property CONFIG_VOLTAGE 3.3 [current_design] |
| |
| set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF] |
| |
| #write_checkpoint -force synth.dcp |
| } |
| |
| |
| proc loc_roi_clk_left {ff_x ff_y} { |
| # Place an ROI clk on the left edge of the ROI |
| # It doesn't actually matter where we place this, just do it to keep things neat looking |
| # ff_x: ROI SLICE X position |
| # ff_yy: row primitives will be placed at |
| |
| set slice_ff "SLICE_X${ff_x}Y${ff_y}" |
| |
| # Fix FFs to guide route in |
| set cell [get_cells "roi/clk_reg_reg"] |
| set_property LOC $slice_ff $cell |
| set_property BEL AFF $cell |
| } |
| |
| proc loc_lut_in {index lut_x lut_y} { |
| # Place a lut at specified coordinates in BEL A |
| # index: input bus index |
| # lut_x: SLICE X position |
| # lut_y: SLICE Y position |
| |
| set slice_lut "SLICE_X${lut_x}Y${lut_y}" |
| |
| # Fix LUTs near the edge |
| set cell [get_cells "roi/ins[$index].lut"] |
| set_property LOC $slice_lut $cell |
| set_property BEL A6LUT $cell |
| } |
| |
| proc loc_lut_out {index lut_x lut_y} { |
| # Place a lut at specified coordinates in BEL A |
| # index: input bus index |
| # lut_x: SLICE X position |
| # lut_y: SLICE Y position |
| |
| set slice_lut "SLICE_X${lut_x}Y${lut_y}" |
| |
| # Fix LUTs near the edge |
| set cell [get_cells "roi/outs[$index].lut"] |
| set_property LOC $slice_lut $cell |
| set_property BEL A6LUT $cell |
| } |
| |
| proc net_bank_left {net} { |
| # return 1 if net goes to a leftmost die IO bank |
| # return 0 if net goes to a rightmost die IO bank |
| |
| set bank [get_property IOBANK [get_ports $net]] |
| set left_banks "14 15 16" |
| set right_banks "34 35" |
| |
| # left |
| if {[lsearch -exact $left_banks $bank] >= 0} { |
| return 1 |
| # right |
| } elseif {[lsearch -exact $right_banks $bank] >= 0} { |
| return 0 |
| } else { |
| error "Bad bank $bank" |
| } |
| } |
| |
| # Manual placement |
| if {$fixed_xdc eq ""} { |
| set x $X_BASE |
| |
| # Place ROI clock right after inputs |
| puts "Placing ROI clock" |
| loc_roi_clk_left $x $Y_CLK_BASE |
| |
| # Place ROI inputs |
| puts "Placing ROI inputs" |
| set y_left $Y_DIN_BASE |
| # Shift y_right up to avoid PCIe block that makes routing hard. |
| set y_right [expr {$Y_DIN_BASE + $Y_OFFSET}] |
| for {set i 0} {$i < $DIN_N} {incr i} { |
| if {[net_bank_left "din[$i]"]} { |
| loc_lut_in $i $URAY_ROI_X0 $y_left |
| set y_left [expr {$y_left + $PITCH}] |
| } else { |
| loc_lut_in $i $URAY_ROI_X1 $y_right |
| set y_right [expr {$y_right + $PITCH}] |
| } |
| } |
| |
| # Place ROI outputs |
| set y_left $Y_DOUT_BASE |
| set y_right $Y_DOUT_BASE |
| puts "Placing ROI outputs" |
| for {set i 0} {$i < $DOUT_N} {incr i} { |
| if {[net_bank_left "dout[$i]"]} { |
| loc_lut_out $i $URAY_ROI_X0 $y_left |
| set y_left [expr {$y_left + $PITCH}] |
| } else { |
| loc_lut_out $i $URAY_ROI_X1 $y_right |
| set y_right [expr {$y_right + $PITCH}] |
| } |
| } |
| } |
| |
| place_design |
| #write_checkpoint -force placed.dcp |
| |
| # Version with more error checking for missing end node |
| # Will do best effort in this case |
| proc route_via2 {net nodes} { |
| # net: net as string |
| # nodes: string list of one or more intermediate routing nodes to visit |
| |
| set net [get_nets $net] |
| # Start at the net source |
| set fixed_route [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]] |
| # End at the net destination |
| # For sone reason this doesn't always show up |
| set site_pins [get_site_pins -filter {DIRECTION == IN} -of_objects $net] |
| if {$site_pins eq ""} { |
| puts "WARNING: could not find end node" |
| #error "Could not find end node" |
| } else { |
| set end_node [get_nodes -of_objects] |
| lappend nodes [$end_node] |
| } |
| |
| puts "" |
| puts "Routing net $net:" |
| |
| foreach to_node $nodes { |
| if {$to_node eq ""} { |
| error "Empty node" |
| } |
| |
| # Node string to object |
| set to_node [get_nodes -of_objects [get_wires $to_node]] |
| # Start at last routed position |
| set from_node [lindex $fixed_route end] |
| # Let vivado do heavy liftin in between |
| set route [find_routing_path -quiet -from $from_node -to $to_node] |
| if {$route == ""} { |
| # Some errors print a huge route |
| puts [concat [string range " $from_node -> $to_node" 0 1000] ": no route found - assuming direct PIP"] |
| lappend fixed_route $to_node |
| } { |
| puts [concat [string range " $from_node -> $to_node: $route" 0 1000] "routed"] |
| set fixed_route [concat $fixed_route [lrange $route 1 end]] |
| } |
| set_property -quiet FIXED_ROUTE $fixed_route $net |
| } |
| |
| set_property -quiet FIXED_ROUTE $fixed_route $net |
| puts "" |
| } |
| |
| # Return the wire on the ROI boundary |
| proc node2wire {node} { |
| set wires [get_wires -of_objects [get_nodes $node]] |
| set wire [lsearch -inline $wires *VBRK*] |
| return $wire |
| } |
| |
| proc write_grid_roi {fp} { |
| puts $fp "GRID_X_MIN = $::env(URAY_ROI_GRID_X1)" |
| puts $fp "GRID_X_MAX = $::env(URAY_ROI_GRID_X2)" |
| puts $fp "GRID_Y_MIN = $::env(URAY_ROI_GRID_Y1)" |
| puts $fp "GRID_Y_MAX = $::env(URAY_ROI_GRID_Y2)" |
| } |
| |
| set fp [open "design_info.txt" w] |
| write_grid_roi $fp |
| close $fp |
| |
| set fp [open "design.txt" w] |
| set fp_wires [open "design_pad_wires.txt" w] |
| puts $fp "name node pin wire" |
| # Manual routing |
| if {$fixed_xdc eq ""} { |
| set x $X_BASE |
| |
| # No routing strictly needed for clk |
| # It will go to high level interconnect that goes everywhere |
| # But we still need to record something, so lets force a route |
| # FIXME: very ROI specific |
| set node "$URAY_ROI_HCLK" |
| set wire [node2wire $node] |
| route_via2 "clk_IBUF_BUFG" "$node" |
| set net "clk" |
| set pin "$net2pin($net)" |
| puts $fp "$net $node $pin $wire" |
| |
| puts "Routing ROI inputs" |
| # Arbitrary offset as observed |
| set y_left $Y_DIN_BASE |
| set y_right [expr {$Y_DIN_BASE + $Y_OFFSET}] |
| for {set i 0} {$i < $DIN_N} {incr i} { |
| # needed to force routes away to avoid looping into ROI |
| if {[net_bank_left "din[$i]"]} { |
| set node "INT_L_X${DIN_INT_L_X}Y${y_left}/${DIN_LPIP}" |
| route_via2 "din_IBUF[$i]" "$node" |
| set y_left [expr {$y_left + $PITCH}] |
| } else { |
| set node "INT_R_X${DIN_INT_R_X}Y${y_right}/${DIN_RPIP}" |
| route_via2 "din_IBUF[$i]" "$node" |
| set y_right [expr {$y_right + $PITCH}] |
| } |
| set net "din[$i]" |
| set pin "$net2pin($net)" |
| set wire [node2wire $node] |
| puts $fp "$net $node $pin $wire" |
| |
| set wires [get_wires -of_objects [get_nets "din_IBUF[$i]"]] |
| puts $fp_wires "$net $pin $wires" |
| } |
| |
| puts "Routing ROI outputs" |
| # Arbitrary offset as observed |
| set y_left [expr {$Y_DOUT_BASE + 0}] |
| set y_right [expr {$Y_DOUT_BASE + 0}] |
| for {set i 0} {$i < $DOUT_N} {incr i} { |
| if {[net_bank_left "dout[$i]"]} { |
| set node "INT_L_X${DOUT_INT_L_X}Y${y_left}/${DOUT_LPIP}" |
| route_via2 "roi/dout[$i]" "$node" |
| set y_left [expr {$y_left + $PITCH}] |
| # XXX: only care about right ports on Arty |
| } else { |
| set node "INT_R_X${DOUT_INT_R_X}Y${y_right}/${DOUT_RPIP}" |
| route_via2 "roi/dout[$i]" "$node" |
| set y_right [expr {$y_right + $PITCH}] |
| } |
| set net "dout[$i]" |
| set pin "$net2pin($net)" |
| set wire [node2wire $node] |
| puts $fp "$net $node $pin $wire" |
| |
| set wires [get_wires -of_objects [get_nets "roi/dout[$i]"]] |
| puts $fp_wires "$net $pin $wires" |
| } |
| } |
| close $fp |
| close $fp_wires |
| |
| puts "routing design" |
| route_design |
| |
| # Don't set for user designs |
| # Makes things easier to debug |
| if {$fixed_xdc eq ""} { |
| set_property IS_ROUTE_FIXED 1 [get_nets -hierarchical] |
| #set_property IS_LOC_FIXED 1 [get_cells -hierarchical] |
| #set_property IS_BEL_FIXED 1 [get_cells -hierarchical] |
| write_xdc -force fixed.xdc |
| } |
| |
| write_checkpoint -force design.dcp |
| #set_property BITSTREAM.GENERAL.DEBUGBITSTREAM YES [current_design] |
| write_bitstream -force design.bit |