mirror of https://github.com/openXC7/prjxray.git
minitest: Add csv generation script
Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
This commit is contained in:
parent
9e4c6f1bb5
commit
e2b88458e8
|
|
@ -3,7 +3,6 @@ all: build/env build/roi_roi_io.diff \
|
|||
build/SLEW/run.ok \
|
||||
build/DRIVE/run.ok \
|
||||
build/IOSTANDARD/run.ok \
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
|
@ -35,3 +34,6 @@ build/DRIVE/run.ok:
|
|||
build/IOSTANDARD/run.ok:
|
||||
PROJECT=IOSTANDARD bash runme_tcl.sh
|
||||
|
||||
csv:
|
||||
PROJECT=$@ bash runme.sh
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,39 @@
|
|||
# PULLTYPE
|
||||
|
||||
PULLTYPE 28 29 30
|
||||
NONE X
|
||||
KEEPER X X
|
||||
PULLTYPE 38_98 39_97 39_97
|
||||
NONE X
|
||||
KEEPER X X
|
||||
PULLDOWN
|
||||
PULLUP X X
|
||||
PULLUP X X
|
||||
|
||||
|
||||
# DRIVE
|
||||
|
||||
DRIVE A00 A02 A08 A10 B09 B01
|
||||
0 FIXME
|
||||
4 X X X
|
||||
8 X X X
|
||||
12 X X X
|
||||
16 X X X
|
||||
24 FIXME
|
||||
Drive strength depends on current IOSTANDARD, e.g.
|
||||
|
||||
LVCMOS18
|
||||
DRIVE 38_64 38_66 38_72 38_74 39_65 39_73
|
||||
4 X X X
|
||||
8 X X X
|
||||
12 X X X
|
||||
16 X X X
|
||||
24 X X X
|
||||
|
||||
LVCMOS25
|
||||
DRIVE 38_64 38_66 38_72 38_74 39_65 39_73
|
||||
4 X X X
|
||||
8 X
|
||||
12
|
||||
16 X X X
|
||||
|
||||
LVCMOS33
|
||||
DRIVE 38_64 38_66 38_72 38_74 39_65 39_73
|
||||
4 X X X
|
||||
8 X X X
|
||||
12 X X X
|
||||
16 X X X
|
||||
|
||||
The minitest contains a csv target which generates a csv with differences across all LVCMOS and LVTTL standards for all supported DRIVE strengths and both slew rates.
|
||||
|
||||
# IOSTANDARD
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,162 @@
|
|||
import sys
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import difflib
|
||||
|
||||
|
||||
def get_file_pairs():
|
||||
pairs_list = list()
|
||||
for path1 in glob.glob('*.bits'):
|
||||
for path2 in glob.glob('*.bits'):
|
||||
file1 = os.path.basename(path1)
|
||||
file2 = os.path.basename(path2)
|
||||
if file1 == file2:
|
||||
continue
|
||||
files_pair = [file1, file2]
|
||||
files_pair.sort()
|
||||
pairs_list.append(files_pair[0] + ":" + files_pair[1])
|
||||
pairs_set = set(pairs_list)
|
||||
for pair in pairs_set:
|
||||
file1, file2 = pair.split(":")
|
||||
yield file1, file2
|
||||
|
||||
|
||||
def extract_parameters_string(basename):
|
||||
params_str = re.search('^design_(.*).bits$', basename)
|
||||
return params_str.group(1)
|
||||
|
||||
|
||||
def extract_parameters(basename):
|
||||
iostandard, slew, drive = extract_parameters_string(basename).split('_')
|
||||
return iostandard, slew, drive
|
||||
|
||||
|
||||
def generate_differing_bits(basename1, basename2):
|
||||
with open(basename1, 'r') as path1:
|
||||
with open(basename2, 'r') as path2:
|
||||
diff = difflib.unified_diff(
|
||||
path1.read().splitlines(),
|
||||
path2.read().splitlines(),
|
||||
fromfile='path1',
|
||||
tofile='path2')
|
||||
for line in diff:
|
||||
if line.startswith('---'):
|
||||
continue
|
||||
if line.startswith('+++'):
|
||||
continue
|
||||
if line.startswith('@'):
|
||||
continue
|
||||
if line.startswith('-'):
|
||||
yield extract_parameters_string(basename1), line.strip('-')
|
||||
continue
|
||||
if line.startswith('+'):
|
||||
yield extract_parameters_string(basename2), line.strip('+')
|
||||
continue
|
||||
|
||||
|
||||
class Database():
|
||||
def __init__(self, convert_bits=False):
|
||||
self.all_bits = set()
|
||||
self.properties_bits = dict()
|
||||
self.convert_bits = convert_bits
|
||||
self.populate()
|
||||
|
||||
def populate(self):
|
||||
for file1, file2 in get_file_pairs():
|
||||
#print(file1 + " vs " + file2)
|
||||
for property_str, bit in generate_differing_bits(file1, file2):
|
||||
#print(property_str + " " + bit)
|
||||
self.update_all_bits(bit)
|
||||
if property_str in self.properties_bits:
|
||||
self.properties_bits[property_str].add(bit)
|
||||
else:
|
||||
self.properties_bits[property_str] = set()
|
||||
self.properties_bits[property_str].add(bit)
|
||||
|
||||
def update_all_bits(self, bit):
|
||||
self.all_bits.add(bit)
|
||||
|
||||
def print_all_bits(self):
|
||||
print(self.all_bits)
|
||||
|
||||
def get_keys(self):
|
||||
return self.properties_bits.keys()
|
||||
|
||||
def print_bits(self, key):
|
||||
if key in self.properties_bits:
|
||||
print("%s: %s" % (key, self.properties_bits[key]))
|
||||
else:
|
||||
print("The specified property is not in the database")
|
||||
|
||||
def convert_bit_format(self, item):
|
||||
dummy, address, word, bit = item.split("_")
|
||||
address = int(address[-2:], 16)
|
||||
bit = int(word) % 4 * 32 + int(bit)
|
||||
return "{address}_{bit}".format(address=address, bit=bit)
|
||||
|
||||
def convert_header(self, header):
|
||||
converted_bits = []
|
||||
for bit in header:
|
||||
#print(bit + ":" + self.convert_bit_format(bit))
|
||||
converted_bits.append(self.convert_bit_format(bit))
|
||||
return converted_bits
|
||||
|
||||
def get_csv_header(self):
|
||||
header = list(self.all_bits)
|
||||
header.sort()
|
||||
self.csv_header = header
|
||||
if self.convert_bits:
|
||||
header = self.convert_header(header)
|
||||
line = "property,v,i,r,"
|
||||
for title in header:
|
||||
line += title + ","
|
||||
return line + '\n'
|
||||
|
||||
def extract_rvi_parameters(self, rvi):
|
||||
iostandard, slew, drive = rvi.split("_")
|
||||
if iostandard[-2:] == "12":
|
||||
voltage = 1.2
|
||||
elif iostandard[-2:] == "15":
|
||||
voltage = 1.5
|
||||
elif iostandard[-2:] == "18":
|
||||
voltage = 1.8
|
||||
elif iostandard[-2:] == "25":
|
||||
voltage = 2.5
|
||||
else:
|
||||
voltage = 3.3
|
||||
resistance = voltage / (int(drive) * 0.001)
|
||||
return "%.1f,%s,%.3f" % (voltage, drive, resistance)
|
||||
|
||||
def get_csv_body(self):
|
||||
lines = ""
|
||||
keys = list(self.get_keys())
|
||||
keys.sort()
|
||||
for properties_key in keys:
|
||||
line = properties_key + "," + self.extract_rvi_parameters(
|
||||
properties_key) + ","
|
||||
for title in self.csv_header:
|
||||
if title in self.properties_bits[properties_key]:
|
||||
line += "X,"
|
||||
else:
|
||||
line += " ,"
|
||||
line += '\n'
|
||||
lines += line
|
||||
return lines
|
||||
|
||||
def write_csv(self, filename):
|
||||
filename = os.getcwd() + "/" + filename
|
||||
fp = open(filename, 'w')
|
||||
fp.write(self.get_csv_header())
|
||||
fp.write(self.get_csv_body())
|
||||
fp.close()
|
||||
print("Written results to %s file.\n" % filename)
|
||||
|
||||
|
||||
def main():
|
||||
database = Database(True)
|
||||
database.write_csv("differences.csv")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
source "$::env(XRAY_DIR)/utils/utils.tcl"
|
||||
|
||||
proc make_io_pin_sites {} {
|
||||
# get all possible IOB pins
|
||||
foreach pad [get_package_pins -filter "IS_GENERAL_PURPOSE == 1"] {
|
||||
set site [get_sites -of_objects $pad]
|
||||
if {[llength $site] == 0} {
|
||||
continue
|
||||
}
|
||||
if [string match IOB33* [get_property SITE_TYPE $site]] {
|
||||
dict append io_pin_sites $site $pad
|
||||
}
|
||||
}
|
||||
return $io_pin_sites
|
||||
}
|
||||
|
||||
proc load_pin_lines {} {
|
||||
# IOB_X0Y103 clk input
|
||||
# IOB_X0Y129 do[0] output
|
||||
|
||||
set fp [open "$::env(SRC_DIR)/params.csv" r]
|
||||
set pin_lines {}
|
||||
for {gets $fp line} {$line != ""} {gets $fp line} {
|
||||
lappend pin_lines [split $line ","]
|
||||
}
|
||||
close $fp
|
||||
return $pin_lines
|
||||
}
|
||||
|
||||
proc loc_pins {} {
|
||||
set pin_lines [load_pin_lines]
|
||||
set io_pin_sites [make_io_pin_sites]
|
||||
|
||||
puts "Looping"
|
||||
foreach line $pin_lines {
|
||||
puts "$line"
|
||||
lassign $line site_str pin_str io cell_str
|
||||
|
||||
# Have: site
|
||||
# Want: pin for site
|
||||
set site [get_sites $site_str]
|
||||
#set pad_bel [get_bels -of_objects $site -filter {TYPE =~ PAD && NAME =~ IOB_*}]
|
||||
# set port [get_ports -of_objects $site]
|
||||
set port [get_ports $pin_str]
|
||||
set tile [get_tiles -of_objects $site]
|
||||
set pin [dict get $io_pin_sites $site]
|
||||
set iostandard [get_property IOSTANDARD $port]
|
||||
|
||||
set_property -dict "PACKAGE_PIN $pin IOSTANDARD $iostandard" $port
|
||||
}
|
||||
}
|
||||
|
||||
proc set_property_value_on_port {property value port} {
|
||||
set_property $property $value $port
|
||||
set got [get_property $property $port]
|
||||
if {"$got" != "$value"} {
|
||||
puts "Skipping: wanted $value, got $got"
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
proc run {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
read_verilog top.v
|
||||
synth_design -top top
|
||||
|
||||
# Mostly doesn't matter since IOB are special, but add anyway
|
||||
create_pblock roi
|
||||
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
|
||||
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
set_param tcl.collectionResultDisplayLimit 0
|
||||
|
||||
loc_pins
|
||||
place_design
|
||||
route_design
|
||||
set pin_lines [load_pin_lines]
|
||||
# For HR Current Drive
|
||||
set property_dictionary [dict create \
|
||||
LVCMOS12 \
|
||||
[dict create DRIVE [list 4 8 12] SLEW [list SLOW FAST]] \
|
||||
LVCMOS15 \
|
||||
[dict create DRIVE [list 4 8 12 16] SLEW [list SLOW FAST]] \
|
||||
LVCMOS18 \
|
||||
[dict create DRIVE [list 4 8 12 16 24] SLEW [list SLOW FAST]] \
|
||||
LVCMOS25 \
|
||||
[dict create DRIVE [list 4 8 12 16] SLEW [list SLOW FAST]] \
|
||||
LVCMOS33 \
|
||||
[dict create DRIVE [list 4 8 12 16] SLEW [list SLOW FAST]] \
|
||||
LVTTL \
|
||||
[dict create DRIVE [list 4 8 12 16 24] SLEW [list SLOW FAST]] \
|
||||
]
|
||||
#HSUL_12 no DRIVE support, only SLEW
|
||||
#HSTL_I, HSTL_II, HSTL_I_18, HSTL_II_18 no drive support, only SLEW
|
||||
#SSTL/18/135/ no drive support, only SLEW
|
||||
|
||||
foreach iostandard [dict keys $property_dictionary] {
|
||||
foreach slew [dict get $property_dictionary $iostandard SLEW] {
|
||||
foreach drive [dict get $property_dictionary $iostandard DRIVE] {
|
||||
foreach line $pin_lines {
|
||||
lassign $line site_str pin_str io cell_str
|
||||
set port [get_ports $pin_str]
|
||||
|
||||
set_property IOSTANDARD $iostandard $port
|
||||
|
||||
if {$io == "input"} continue
|
||||
|
||||
if {[set_property_value_on_port SLEW $slew $port]} {
|
||||
continue
|
||||
}
|
||||
|
||||
if {[set_property_value_on_port DRIVE $drive $port]} {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if {[catch {write_bitstream -force design_${iostandard}_${slew}_${drive}.bit} issue]} {
|
||||
puts "WARNING failed to write: $issue"
|
||||
continue
|
||||
}
|
||||
# Only write checkpoints for acceptable bitstreams
|
||||
write_checkpoint -force design_${iostandard}_${slew}_${drive}.dcp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
run
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
IOB_X0Y111,di[0],input,di_bufs[0].ibuf
|
||||
IOB_X0Y107,do[0],output,do_bufs[0].obuf
|
||||
|
|
|
@ -6,13 +6,14 @@ set -ex
|
|||
|
||||
# Create build dir
|
||||
export SRC_DIR=$PWD
|
||||
BUILD_DIR=build/$PROJECT
|
||||
export BUILD_DIR=build/$PROJECT
|
||||
mkdir -p $BUILD_DIR
|
||||
cd $BUILD_DIR
|
||||
python3 ${SRC_DIR}/top.py > top.v
|
||||
|
||||
export TOP_V=$SRC_DIR/top.v
|
||||
|
||||
${XRAY_VIVADO} -mode batch -source $SRC_DIR/runme.tcl
|
||||
${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o design.bits -z -y design.bit
|
||||
test -z "$(fgrep CRITICAL vivado.log)"
|
||||
|
||||
${XRAY_VIVADO} -mode batch -source $SRC_DIR/generate.tcl
|
||||
for x in design*.bit; do
|
||||
${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o ${x}s -z -y $x
|
||||
done
|
||||
test -z "$(fgrep CRITICAL vivado.log)" && touch run.ok
|
||||
python3 ${SRC_DIR}/compare.py
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
'''
|
||||
Generate a primitive to place at every I/O
|
||||
Unlike CLB tests, the LFSR for this is inside the ROI, not driving it
|
||||
'''
|
||||
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
#random.seed(int(os.getenv("SEED"), 16))
|
||||
from prjxray import util
|
||||
from prjxray import verilog
|
||||
|
||||
|
||||
def gen_iobs():
|
||||
'''
|
||||
IOB33S: main IOB of a diff pair
|
||||
IOB33M: secondary IOB of a diff pair
|
||||
IOB33: not a diff pair. Relatively rare (at least in ROI...2 of them?)
|
||||
Focus on IOB33S to start
|
||||
'''
|
||||
for _tile_name, site_name, site_type in util.get_roi().gen_sites(
|
||||
#['IOB33', 'IOB33S', 'IOB33M']):
|
||||
['IOB33S']):
|
||||
yield site_name, site_type
|
||||
|
||||
|
||||
def write_pins(ports):
|
||||
pinstr = ''
|
||||
for site, (name, dir_, cell) in sorted(ports.items(), key=lambda x: x[1]):
|
||||
# pinstr += 'set_property -dict "PACKAGE_PIN %s IOSTANDARD LVCMOS33" [get_ports %s]' % (packpin, port)
|
||||
pinstr += '%s,%s,%s,%s\n' % (site, name, dir_, cell)
|
||||
open('params.csv', 'w').write(pinstr)
|
||||
|
||||
|
||||
def run():
|
||||
# All possible values
|
||||
iosites = {}
|
||||
for site_name, site_type in gen_iobs():
|
||||
iosites[site_name] = site_type
|
||||
|
||||
# Assigned in this design
|
||||
ports = {}
|
||||
DIN_N = 0
|
||||
DOUT_N = 0
|
||||
|
||||
def remain_sites():
|
||||
return set(iosites.keys()) - set(ports.keys())
|
||||
|
||||
def rand_site():
|
||||
'''Get a random, unused site'''
|
||||
return random.choice(list(remain_sites()))
|
||||
|
||||
def get_site():
|
||||
return next(iter(remain_sites()))
|
||||
|
||||
def assign_i(site, name):
|
||||
nonlocal DIN_N
|
||||
|
||||
assert site not in ports
|
||||
cell = "di_bufs[%u].ibuf" % DIN_N
|
||||
DIN_N += 1
|
||||
ports[site] = (name, 'input', cell)
|
||||
|
||||
def assign_o(site, name):
|
||||
nonlocal DOUT_N
|
||||
|
||||
assert site not in ports
|
||||
cell = "do_bufs[%u].obuf" % DOUT_N
|
||||
DOUT_N += 1
|
||||
ports[site] = (name, 'output', cell)
|
||||
|
||||
# Assign at least one di and one do
|
||||
assign_i(get_site(), 'di[0]')
|
||||
assign_o(get_site(), 'do[0]')
|
||||
# Now assign the rest randomly
|
||||
#while len(remain_sites()):
|
||||
# assign_o(rand_site(), 'do[%u]' % DOUT_N)
|
||||
|
||||
#write_pins(ports)
|
||||
|
||||
print(
|
||||
'''
|
||||
`define N_DI %u
|
||||
`define N_DO %u
|
||||
|
||||
module top(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do);
|
||||
genvar i;
|
||||
|
||||
//Instantiate BUFs so we can LOC them
|
||||
|
||||
wire [`N_DI-1:0] di_buf;
|
||||
generate
|
||||
for (i = 0; i < `N_DI; i = i+1) begin:di_bufs
|
||||
IBUF #(
|
||||
) ibuf(.I(di[i]), .O(di_buf[i]));
|
||||
end
|
||||
endgenerate
|
||||
|
||||
wire [`N_DO-1:0] do_unbuf;
|
||||
generate
|
||||
for (i = 0; i < `N_DO; i = i+1) begin:do_bufs
|
||||
OBUF #(
|
||||
) obuf(.I(do_unbuf[i]), .O(do[i]));
|
||||
end
|
||||
endgenerate
|
||||
|
||||
roi roi(.di(di_buf), .do(do_unbuf));
|
||||
endmodule
|
||||
|
||||
//Arbitrary terminate into LUTs
|
||||
module roi(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do);
|
||||
genvar i;
|
||||
|
||||
generate
|
||||
for (i = 0; i < `N_DI; i = i+1) begin:dis
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
LUT6 #(
|
||||
.INIT(64'h8000_0000_0000_0001)
|
||||
) lut (
|
||||
.I0(di[i]),
|
||||
.I1(di[i]),
|
||||
.I2(di[i]),
|
||||
.I3(di[i]),
|
||||
.I4(di[i]),
|
||||
.I5(di[i]),
|
||||
.O());
|
||||
end
|
||||
endgenerate
|
||||
|
||||
generate
|
||||
for (i = 0; i < `N_DO; i = i+1) begin:dos
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
LUT6 #(
|
||||
.INIT(64'h8000_0000_0000_0001)
|
||||
) lut (
|
||||
.I0(),
|
||||
.I1(),
|
||||
.I2(),
|
||||
.I3(),
|
||||
.I4(),
|
||||
.I5(),
|
||||
.O(do[i]));
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
''' % (DIN_N, DOUT_N))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
Loading…
Reference in New Issue