mirror of https://github.com/YosysHQ/icestorm.git
PLL configuration fuzzing script
This commit is contained in:
parent
b78417ee78
commit
3059607dd7
|
|
@ -0,0 +1 @@
|
|||
work_pllauto/
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
"PLLTYPE_1": (14, 31, "PLLCONFIG_1"),
|
||||
"PLLTYPE_2": (14, 31, "PLLCONFIG_3"),
|
||||
"PLLTYPE_0": (12, 31, "PLLCONFIG_5"),
|
||||
"FEEDBACK_PATH_0": (14, 31, "PLLCONFIG_5"),
|
||||
"FEEDBACK_PATH_1": (11, 31, "PLLCONFIG_9"),
|
||||
"FEEDBACK_PATH_2": (12, 31, "PLLCONFIG_1"),
|
||||
"PLLOUT_SELECT_A_0": (12, 31, "PLLCONFIG_6"),
|
||||
"PLLOUT_SELECT_A_1": (12, 31, "PLLCONFIG_7"),
|
||||
"PLLOUT_SELECT_B_0": (12, 31, "PLLCONFIG_2"),
|
||||
"PLLOUT_SELECT_B_1": (12, 31, "PLLCONFIG_3"),
|
||||
"SHIFTREG_DIV_MODE": (12, 31, "PLLCONFIG_4"),
|
||||
"FDA_FEEDBACK_0": (12, 31, "PLLCONFIG_9"),
|
||||
"FDA_FEEDBACK_1": (13, 31, "PLLCONFIG_1"),
|
||||
"FDA_FEEDBACK_2": (13, 31, "PLLCONFIG_2"),
|
||||
"FDA_FEEDBACK_3": (13, 31, "PLLCONFIG_3"),
|
||||
"FDA_RELATIVE_0": (13, 31, "PLLCONFIG_5"),
|
||||
"FDA_RELATIVE_1": (13, 31, "PLLCONFIG_6"),
|
||||
"FDA_RELATIVE_2": (13, 31, "PLLCONFIG_7"),
|
||||
"FDA_RELATIVE_3": (13, 31, "PLLCONFIG_8"),
|
||||
"DIVR_0": (10, 31, "PLLCONFIG_1"),
|
||||
"DIVR_1": (10, 31, "PLLCONFIG_2"),
|
||||
"DIVR_2": (10, 31, "PLLCONFIG_3"),
|
||||
"DIVR_3": (10, 31, "PLLCONFIG_4"),
|
||||
"DIVF_0": (10, 31, "PLLCONFIG_5"),
|
||||
"DIVF_1": (10, 31, "PLLCONFIG_6"),
|
||||
"DIVF_2": (10, 31, "PLLCONFIG_7"),
|
||||
"DIVF_3": (10, 31, "PLLCONFIG_8"),
|
||||
"DIVF_4": (10, 31, "PLLCONFIG_9"),
|
||||
"DIVF_5": (11, 31, "PLLCONFIG_1"),
|
||||
"DIVF_6": (11, 31, "PLLCONFIG_2"),
|
||||
"DIVQ_0": (11, 31, "PLLCONFIG_3"),
|
||||
"DIVQ_1": (11, 31, "PLLCONFIG_4"),
|
||||
"DIVQ_2": (11, 31, "PLLCONFIG_5"),
|
||||
"FILTER_RANGE_0": (11, 31, "PLLCONFIG_6"),
|
||||
"FILTER_RANGE_1": (11, 31, "PLLCONFIG_7"),
|
||||
"FILTER_RANGE_2": (11, 31, "PLLCONFIG_8"),
|
||||
"TEST_MODE": (12, 31, "PLLCONFIG_8"),
|
||||
"DELAY_ADJMODE_FB": (13, 31, "PLLCONFIG_4"),
|
||||
"DELAY_ADJMODE_REL": (13, 31, "PLLCONFIG_9"),
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os, sys
|
||||
# PLL automatic fuzzing script (WIP)
|
||||
|
||||
device = "up5k"
|
||||
|
||||
# PLL config bits to be fuzzed
|
||||
# These must be in an order such that a config with bit i set doesn't set any other undiscovered bits yet
|
||||
# e.g. PLL_TYPE must be fuzzed first as these will need to be set later on by virtue of enabling the PLL
|
||||
|
||||
fuzz_bits = [
|
||||
"PLLTYPE_1",
|
||||
"PLLTYPE_2",
|
||||
"PLLTYPE_0", #NB: as per the rule above this comes later is it can only be set by also setting 1 or 2
|
||||
|
||||
"FEEDBACK_PATH_0",
|
||||
"FEEDBACK_PATH_1",
|
||||
"FEEDBACK_PATH_2",
|
||||
|
||||
"PLLOUT_SELECT_A_0",
|
||||
"PLLOUT_SELECT_A_1",
|
||||
|
||||
"PLLOUT_SELECT_B_0",
|
||||
"PLLOUT_SELECT_B_1",
|
||||
|
||||
"SHIFTREG_DIV_MODE",
|
||||
|
||||
"FDA_FEEDBACK_0",
|
||||
"FDA_FEEDBACK_1",
|
||||
"FDA_FEEDBACK_2",
|
||||
"FDA_FEEDBACK_3",
|
||||
|
||||
"FDA_RELATIVE_0",
|
||||
"FDA_RELATIVE_1",
|
||||
"FDA_RELATIVE_2",
|
||||
"FDA_RELATIVE_3",
|
||||
|
||||
"DIVR_0",
|
||||
"DIVR_1",
|
||||
"DIVR_2",
|
||||
"DIVR_3",
|
||||
|
||||
"DIVF_0",
|
||||
"DIVF_1",
|
||||
"DIVF_2",
|
||||
"DIVF_3",
|
||||
"DIVF_4",
|
||||
"DIVF_5",
|
||||
"DIVF_6",
|
||||
|
||||
#DIVQ_0 is missing, see comments later on
|
||||
"DIVQ_1",
|
||||
"DIVQ_2",
|
||||
|
||||
"FILTER_RANGE_0",
|
||||
"FILTER_RANGE_1",
|
||||
"FILTER_RANGE_2",
|
||||
|
||||
"TEST_MODE",
|
||||
|
||||
"DELAY_ADJMODE_FB", #these come at the end in case they set FDA_RELATIVE??
|
||||
"DELAY_ADJMODE_REL"
|
||||
]
|
||||
|
||||
# Boilerplate code based on the icefuzz script
|
||||
code_prefix = """
|
||||
module top(packagepin, a, b, w, x, y, z, extfeedback, bypass, resetb, lock, latchinputvalue, sdi, sdo, sclk, dynamicdelay_0, dynamicdelay_1, dynamicdelay_2, dynamicdelay_3, dynamicdelay_4, dynamicdelay_5, dynamicdelay_6, dynamicdelay_7);
|
||||
input packagepin;
|
||||
input a;
|
||||
input b;
|
||||
output w;
|
||||
output x;
|
||||
output reg y;
|
||||
output reg z;
|
||||
input extfeedback;
|
||||
input bypass;
|
||||
input resetb;
|
||||
output lock;
|
||||
input latchinputvalue;
|
||||
input sdi;
|
||||
output sdo;
|
||||
input sclk;
|
||||
wire plloutcorea;
|
||||
wire plloutcoreb;
|
||||
wire plloutglobala;
|
||||
wire plloutglobalb;
|
||||
assign w = plloutcorea ^ a;
|
||||
assign x = plloutcoreb ^ b;
|
||||
always @(posedge plloutglobala) y <= a;
|
||||
always @(posedge plloutglobalb) z <= b;
|
||||
input dynamicdelay_0;
|
||||
input dynamicdelay_1;
|
||||
input dynamicdelay_2;
|
||||
input dynamicdelay_3;
|
||||
input dynamicdelay_4;
|
||||
input dynamicdelay_5;
|
||||
input dynamicdelay_6;
|
||||
input dynamicdelay_7;
|
||||
"""
|
||||
|
||||
def get_param_value(param_name, param_size, fuzz_bit):
|
||||
param = str(param_size) + "'b";
|
||||
for i in range(param_size - 1, -1, -1):
|
||||
if fuzz_bit == param_name + "_" + str(i):
|
||||
param += '1'
|
||||
else:
|
||||
param += '0'
|
||||
return param
|
||||
def inst_pll(fuzz_bit):
|
||||
pll_type = "SB_PLL40_2F_PAD" #default to this as it's most flexible
|
||||
|
||||
if fuzz_bit == "PLLTYPE_0":
|
||||
pll_type = "SB_PLL40_CORE"
|
||||
elif fuzz_bit == "PLLTYPE_1":
|
||||
pll_type = "SB_PLL40_PAD"
|
||||
elif fuzz_bit == "PLLTYPE_2":
|
||||
pll_type = "SB_PLL40_2_PAD"
|
||||
|
||||
v = pll_type + " pll_inst (\n"
|
||||
if pll_type == "SB_PLL40_CORE":
|
||||
v += "\t.REFERENCECLK(referenceclk), \n"
|
||||
else:
|
||||
v += "\t.PACKAGEPIN(packagepin), \n"
|
||||
v += "\t.RESETB(resetb),\n"
|
||||
v += "\t.BYPASS(bypass),\n"
|
||||
v += "\t.EXTFEEDBACK(extfeedback),\n"
|
||||
v += "\t.LOCK(lock),\n"
|
||||
v += "\t.LATCHINPUTVALUE(latchinputvalue),\n"
|
||||
v += "\t.SDI(sdi),\n"
|
||||
v += "\t.SDO(sdo),\n"
|
||||
v += "\t.SCLK(sclk),\n"
|
||||
if pll_type == "SB_PLL40_2F_PAD" or pll_type == "SB_PLL40_2_PAD":
|
||||
v += "\t.PLLOUTCOREA(plloutcorea),\n"
|
||||
v += "\t.PLLOUTGLOBALA(plloutglobala),\n"
|
||||
v += "\t.PLLOUTCOREB(plloutcoreb),\n"
|
||||
v += "\t.PLLOUTGLOBALB(plloutglobalb),\n"
|
||||
else:
|
||||
v += "\t.PLLOUTCORE(plloutcorea),\n"
|
||||
v += "\t.PLLOUTGLOBAL(plloutglobala),\n"
|
||||
v += "\t.DYNAMICDELAY({dynamicdelay_7, dynamicdelay_6, dynamicdelay_5, dynamicdelay_4, dynamicdelay_3, dynamicdelay_2, dynamicdelay_1, dynamicdelay_0})\n"
|
||||
v += ");\n"
|
||||
|
||||
v += "defparam pll_inst.DIVR = " + get_param_value("DIVR", 4, fuzz_bit) + ";\n"
|
||||
v += "defparam pll_inst.DIVF = " + get_param_value("DIVF", 7, fuzz_bit) + ";\n"
|
||||
v += "defparam pll_inst.DIVQ = " + get_param_value("DIVQ", 3, fuzz_bit) + ";\n"
|
||||
v += "defparam pll_inst.FILTER_RANGE = " + get_param_value("FILTER_RANGE", 3, fuzz_bit) + ";\n"
|
||||
|
||||
if fuzz_bit == "FEEDBACK_PATH_0":
|
||||
v += "defparam pll_inst.FEEDBACK_PATH = \"SIMPLE\";\n"
|
||||
elif fuzz_bit == "FEEDBACK_PATH_1":
|
||||
v += "defparam pll_inst.FEEDBACK_PATH = \"PHASE_AND_DELAY\";\n"
|
||||
elif fuzz_bit == "FEEDBACK_PATH_2":
|
||||
v += "defparam pll_inst.FEEDBACK_PATH = \"EXTERNAL\";\n"
|
||||
else:
|
||||
v += "defparam pll_inst.FEEDBACK_PATH = \"DELAY\";\n"
|
||||
|
||||
v += "defparam pll_inst.DELAY_ADJUSTMENT_MODE_FEEDBACK = \"" + ("DYNAMIC" if (fuzz_bit == "DELAY_ADJMODE_FB") else "FIXED") + "\";\n"
|
||||
v += "defparam pll_inst.FDA_FEEDBACK = " + get_param_value("FDA_FEEDBACK", 4, fuzz_bit) + ";\n"
|
||||
v += "defparam pll_inst.DELAY_ADJUSTMENT_MODE_RELATIVE = \"" + ("DYNAMIC" if (fuzz_bit == "DELAY_ADJMODE_REL") else "FIXED") + "\";\n"
|
||||
v += "defparam pll_inst.FDA_RELATIVE = " + get_param_value("FDA_RELATIVE", 4, fuzz_bit) + ";\n"
|
||||
v += "defparam pll_inst.SHIFTREG_DIV_MODE = " + ("1'b1" if (fuzz_bit == "SHIFTREG_DIV_MODE") else "1'b0") + ";\n"
|
||||
|
||||
|
||||
|
||||
if pll_type == "SB_PLL40_2F_PAD" or pll_type == "SB_PLL40_2_PAD":
|
||||
if pll_type == "SB_PLL40_2F_PAD":
|
||||
if fuzz_bit == "PLLOUT_SELECT_A_0":
|
||||
v += "defparam pll_inst.PLLOUT_SELECT_PORTA = \"GENCLK_HALF\";\n"
|
||||
elif fuzz_bit == "PLLOUT_SELECT_A_1":
|
||||
v += "defparam pll_inst.PLLOUT_SELECT_PORTA = \"SHIFTREG_90deg\";\n"
|
||||
else:
|
||||
v += "defparam pll_inst.PLLOUT_SELECT_PORTA = \"GENCLK\";\n"
|
||||
if fuzz_bit == "PLLOUT_SELECT_B_0":
|
||||
v += "defparam pll_inst.PLLOUT_SELECT_PORTB = \"GENCLK_HALF\";\n"
|
||||
elif fuzz_bit == "PLLOUT_SELECT_B_1":
|
||||
v += "defparam pll_inst.PLLOUT_SELECT_PORTB = \"SHIFTREG_90deg\";\n"
|
||||
else:
|
||||
v += "defparam pll_inst.PLLOUT_SELECT_PORTB = \"GENCLK\";\n"
|
||||
else:
|
||||
if fuzz_bit == "PLLOUT_SELECT_A_0":
|
||||
v += "defparam pll_inst.PLLOUT_SELECT = \"GENCLK_HALF\";\n"
|
||||
elif fuzz_bit == "PLLOUT_SELECT_A_1":
|
||||
v += "defparam pll_inst.PLLOUT_SELECT = \"SHIFTREG_90deg\";\n"
|
||||
else:
|
||||
v += "defparam pll_inst.PLLOUT_SELECT = \"GENCLK\";\n"
|
||||
v += "defparam pll_inst.TEST_MODE = " + ("1'b1" if (fuzz_bit == "TEST_MODE") else "1'b0") + ";\n"
|
||||
|
||||
return v;
|
||||
|
||||
def make_vlog(fuzz_bit):
|
||||
vlog = code_prefix
|
||||
vlog += inst_pll(fuzz_bit)
|
||||
vlog += "endmodule"
|
||||
return vlog
|
||||
|
||||
known_bits = []
|
||||
|
||||
# Set to true to continue even if multiple bits are changed (needed because
|
||||
# of the issue discusssed below)
|
||||
show_all_bits = False #TODO: make this an argument
|
||||
|
||||
device = "up5k" #TODO: environment variable?
|
||||
|
||||
#HACK: icecube doesn't let you set all of the DIVQ bits to 0,
|
||||
#which makes fuzzing early on annoying as there is never a case
|
||||
#with just 1 bit set. So a tiny bit of semi-manual work is needed
|
||||
#first to discover this (basically run this script with show_all_bits=True
|
||||
#and look for the stuck bit)
|
||||
#TODO: clever code could get rid of this
|
||||
divq_bit0 = {
|
||||
"up5k" : (11, 31, 3)
|
||||
}
|
||||
|
||||
#Return a list of PLL config bits in the format (x, y, bit)
|
||||
def parse_exp(expfile):
|
||||
current_x = 0
|
||||
current_y = 0
|
||||
bits = []
|
||||
with open(expfile, 'r') as f:
|
||||
for line in f:
|
||||
splitline = line.split(' ')
|
||||
if splitline[0] == ".io_tile":
|
||||
current_x = int(splitline[1])
|
||||
current_y = int(splitline[2])
|
||||
elif splitline[0] == "PLL":
|
||||
if splitline[1][:10] == "PLLCONFIG_":
|
||||
bitidx = int(splitline[1][10:])
|
||||
bits.append((current_x, current_y, bitidx))
|
||||
return bits
|
||||
|
||||
#Convert a bit tuple as returned from the above to a nice string
|
||||
def bit_to_str(bit):
|
||||
return "(%d, %d, \"PLLCONFIG_%d\")" % bit
|
||||
|
||||
#The main fuzzing function
|
||||
def do_fuzz():
|
||||
if not os.path.exists("./work_pllauto"):
|
||||
os.mkdir("./work_pllauto")
|
||||
known_bits.append(divq_bit0[device])
|
||||
with open("pll_data_" + device + ".txt", 'w') as dat:
|
||||
for fuzz_bit in fuzz_bits:
|
||||
vlog = make_vlog(fuzz_bit)
|
||||
with open("./work_pllauto/pllauto.v", 'w') as f:
|
||||
f.write(vlog)
|
||||
retval = os.system("bash ../../icecube.sh -" + device + " ./work_pllauto/pllauto.v > ./work_pllauto/icecube.log 2>&1")
|
||||
if retval != 0:
|
||||
sys.stderr.write('ERROR: icecube returned non-zero error code\n')
|
||||
sys.exit(1)
|
||||
retval = os.system("../../../icebox/icebox_explain.py ./work_pllauto/pllauto.asc > ./work_pllauto/pllauto.exp")
|
||||
if retval != 0:
|
||||
sys.stderr.write('ERROR: icebox_explain returned non-zero error code\n')
|
||||
sys.exit(1)
|
||||
pll_bits = parse_exp("./work_pllauto/pllauto.exp")
|
||||
new_bits = []
|
||||
for set_bit in pll_bits:
|
||||
if not (set_bit in known_bits):
|
||||
new_bits.append(set_bit)
|
||||
if len(new_bits) == 0:
|
||||
sys.stderr.write('ERROR: no new bits set when setting config bit ' + fuzz_bit + '\n')
|
||||
sys.exit(1)
|
||||
if len(new_bits) > 1:
|
||||
sys.stderr.write('ERROR: multiple new bits set when setting config bit ' + fuzz_bit + '\n')
|
||||
for bit in new_bits:
|
||||
sys.stderr.write('\t' + bit_to_str(bit) + '\n')
|
||||
if not show_all_bits:
|
||||
sys.exit(1)
|
||||
if len(new_bits) == 1:
|
||||
known_bits.append(new_bits[0])
|
||||
#print DIVQ_0 at the right moment, as it's not fuzzed normally
|
||||
if fuzz_bit == "DIVQ_1":
|
||||
print(("\"DIVQ_0\":").ljust(24) + bit_to_str(divq_bit0[device]) + ",")
|
||||
dat.write(("\"DIVQ_0\":").ljust(24) + bit_to_str(divq_bit0[device]) + ",\n")
|
||||
print(("\"" + fuzz_bit + "\":").ljust(24) + bit_to_str(new_bits[0]) + ",")
|
||||
dat.write(("\"" + fuzz_bit + "\":").ljust(24) + bit_to_str(new_bits[0]) + ",\n")
|
||||
do_fuzz()
|
||||
Loading…
Reference in New Issue