minitest: Add csv generation script

Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
This commit is contained in:
Tomasz Michalak 2019-02-06 10:37:03 +01:00
parent 9e4c6f1bb5
commit e2b88458e8
7 changed files with 483 additions and 19 deletions

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -0,0 +1,2 @@
IOB_X0Y111,di[0],input,di_bufs[0].ibuf
IOB_X0Y107,do[0],output,do_bufs[0].obuf
1 IOB_X0Y111 di[0] input di_bufs[0].ibuf
2 IOB_X0Y107 do[0] output do_bufs[0].obuf

View File

@ -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

View File

@ -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()