1#!/usr/bin/env python3 2 3import os, sys 4# PLL automatic fuzzing script (WIP) 5 6device = "up5k" 7 8# PLL config bits to be fuzzed 9# These must be in an order such that a config with bit i set doesn't set any other undiscovered bits yet 10# e.g. PLL_TYPE must be fuzzed first as these will need to be set later on by virtue of enabling the PLL 11 12fuzz_bits = [ 13 "PLLTYPE_1", 14 "PLLTYPE_2", 15 "PLLTYPE_0", #NB: as per the rule above this comes later is it can only be set by also setting 1 or 2 16 17 "FEEDBACK_PATH_0", 18 "FEEDBACK_PATH_1", 19 "FEEDBACK_PATH_2", 20 21 "PLLOUT_SELECT_A_0", 22 "PLLOUT_SELECT_A_1", 23 24 "PLLOUT_SELECT_B_0", 25 "PLLOUT_SELECT_B_1", 26 27 "SHIFTREG_DIV_MODE", 28 29 "FDA_FEEDBACK_0", 30 "FDA_FEEDBACK_1", 31 "FDA_FEEDBACK_2", 32 "FDA_FEEDBACK_3", 33 34 "FDA_RELATIVE_0", 35 "FDA_RELATIVE_1", 36 "FDA_RELATIVE_2", 37 "FDA_RELATIVE_3", 38 39 "DIVR_0", 40 "DIVR_1", 41 "DIVR_2", 42 "DIVR_3", 43 44 "DIVF_0", 45 "DIVF_1", 46 "DIVF_2", 47 "DIVF_3", 48 "DIVF_4", 49 "DIVF_5", 50 "DIVF_6", 51 52 #DIVQ_0 is missing, see comments later on 53 "DIVQ_1", 54 "DIVQ_2", 55 56 "FILTER_RANGE_0", 57 "FILTER_RANGE_1", 58 "FILTER_RANGE_2", 59 60 "TEST_MODE", 61 62 "DELAY_ADJMODE_FB", #these come at the end in case they set FDA_RELATIVE?? 63 "DELAY_ADJMODE_REL" 64] 65 66# Boilerplate code based on the icefuzz script 67code_prefix = """ 68module 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); 69input packagepin; 70input a; 71input b; 72output w; 73output x; 74output reg y; 75output reg z; 76input extfeedback; 77input bypass; 78input resetb; 79output lock; 80input latchinputvalue; 81input sdi; 82output sdo; 83input sclk; 84wire plloutcorea; 85wire plloutcoreb; 86wire plloutglobala; 87wire plloutglobalb; 88assign w = plloutcorea ^ a; 89assign x = plloutcoreb ^ b; 90always @(posedge plloutglobala) y <= a; 91always @(posedge plloutglobalb) z <= b; 92input dynamicdelay_0; 93input dynamicdelay_1; 94input dynamicdelay_2; 95input dynamicdelay_3; 96input dynamicdelay_4; 97input dynamicdelay_5; 98input dynamicdelay_6; 99input dynamicdelay_7; 100""" 101 102def get_param_value(param_name, param_size, fuzz_bit): 103 param = str(param_size) + "'b"; 104 for i in range(param_size - 1, -1, -1): 105 if fuzz_bit == param_name + "_" + str(i): 106 param += '1' 107 else: 108 param += '0' 109 return param 110def inst_pll(fuzz_bit): 111 pll_type = "SB_PLL40_2F_PAD" #default to this as it's most flexible 112 113 if fuzz_bit == "PLLTYPE_0": 114 pll_type = "SB_PLL40_CORE" 115 elif fuzz_bit == "PLLTYPE_1": 116 pll_type = "SB_PLL40_PAD" 117 elif fuzz_bit == "PLLTYPE_2": 118 pll_type = "SB_PLL40_2_PAD" 119 120 v = pll_type + " pll_inst (\n" 121 if pll_type == "SB_PLL40_CORE": 122 v += "\t.REFERENCECLK(referenceclk), \n" 123 else: 124 v += "\t.PACKAGEPIN(packagepin), \n" 125 v += "\t.RESETB(resetb),\n" 126 v += "\t.BYPASS(bypass),\n" 127 v += "\t.EXTFEEDBACK(extfeedback),\n" 128 v += "\t.LOCK(lock),\n" 129 v += "\t.LATCHINPUTVALUE(latchinputvalue),\n" 130 v += "\t.SDI(sdi),\n" 131 v += "\t.SDO(sdo),\n" 132 v += "\t.SCLK(sclk),\n" 133 if pll_type == "SB_PLL40_2F_PAD" or pll_type == "SB_PLL40_2_PAD": 134 v += "\t.PLLOUTCOREA(plloutcorea),\n" 135 v += "\t.PLLOUTGLOBALA(plloutglobala),\n" 136 v += "\t.PLLOUTCOREB(plloutcoreb),\n" 137 v += "\t.PLLOUTGLOBALB(plloutglobalb),\n" 138 else: 139 v += "\t.PLLOUTCORE(plloutcorea),\n" 140 v += "\t.PLLOUTGLOBAL(plloutglobala),\n" 141 v += "\t.DYNAMICDELAY({dynamicdelay_7, dynamicdelay_6, dynamicdelay_5, dynamicdelay_4, dynamicdelay_3, dynamicdelay_2, dynamicdelay_1, dynamicdelay_0})\n" 142 v += ");\n" 143 144 v += "defparam pll_inst.DIVR = " + get_param_value("DIVR", 4, fuzz_bit) + ";\n" 145 v += "defparam pll_inst.DIVF = " + get_param_value("DIVF", 7, fuzz_bit) + ";\n" 146 v += "defparam pll_inst.DIVQ = " + get_param_value("DIVQ", 3, fuzz_bit) + ";\n" 147 v += "defparam pll_inst.FILTER_RANGE = " + get_param_value("FILTER_RANGE", 3, fuzz_bit) + ";\n" 148 149 if fuzz_bit == "FEEDBACK_PATH_0": 150 v += "defparam pll_inst.FEEDBACK_PATH = \"SIMPLE\";\n" 151 elif fuzz_bit == "FEEDBACK_PATH_1": 152 v += "defparam pll_inst.FEEDBACK_PATH = \"PHASE_AND_DELAY\";\n" 153 elif fuzz_bit == "FEEDBACK_PATH_2": 154 v += "defparam pll_inst.FEEDBACK_PATH = \"EXTERNAL\";\n" 155 else: 156 v += "defparam pll_inst.FEEDBACK_PATH = \"DELAY\";\n" 157 158 v += "defparam pll_inst.DELAY_ADJUSTMENT_MODE_FEEDBACK = \"" + ("DYNAMIC" if (fuzz_bit == "DELAY_ADJMODE_FB") else "FIXED") + "\";\n" 159 v += "defparam pll_inst.FDA_FEEDBACK = " + get_param_value("FDA_FEEDBACK", 4, fuzz_bit) + ";\n" 160 v += "defparam pll_inst.DELAY_ADJUSTMENT_MODE_RELATIVE = \"" + ("DYNAMIC" if (fuzz_bit == "DELAY_ADJMODE_REL") else "FIXED") + "\";\n" 161 v += "defparam pll_inst.FDA_RELATIVE = " + get_param_value("FDA_RELATIVE", 4, fuzz_bit) + ";\n" 162 v += "defparam pll_inst.SHIFTREG_DIV_MODE = " + ("1'b1" if (fuzz_bit == "SHIFTREG_DIV_MODE") else "1'b0") + ";\n" 163 164 165 166 if pll_type == "SB_PLL40_2F_PAD" or pll_type == "SB_PLL40_2_PAD": 167 if pll_type == "SB_PLL40_2F_PAD": 168 if fuzz_bit == "PLLOUT_SELECT_A_0": 169 v += "defparam pll_inst.PLLOUT_SELECT_PORTA = \"GENCLK_HALF\";\n" 170 elif fuzz_bit == "PLLOUT_SELECT_A_1": 171 v += "defparam pll_inst.PLLOUT_SELECT_PORTA = \"SHIFTREG_90deg\";\n" 172 else: 173 v += "defparam pll_inst.PLLOUT_SELECT_PORTA = \"GENCLK\";\n" 174 if fuzz_bit == "PLLOUT_SELECT_B_0": 175 v += "defparam pll_inst.PLLOUT_SELECT_PORTB = \"GENCLK_HALF\";\n" 176 elif fuzz_bit == "PLLOUT_SELECT_B_1": 177 v += "defparam pll_inst.PLLOUT_SELECT_PORTB = \"SHIFTREG_90deg\";\n" 178 else: 179 v += "defparam pll_inst.PLLOUT_SELECT_PORTB = \"GENCLK\";\n" 180 else: 181 if fuzz_bit == "PLLOUT_SELECT_A_0": 182 v += "defparam pll_inst.PLLOUT_SELECT = \"GENCLK_HALF\";\n" 183 elif fuzz_bit == "PLLOUT_SELECT_A_1": 184 v += "defparam pll_inst.PLLOUT_SELECT = \"SHIFTREG_90deg\";\n" 185 else: 186 v += "defparam pll_inst.PLLOUT_SELECT = \"GENCLK\";\n" 187 v += "defparam pll_inst.TEST_MODE = " + ("1'b1" if (fuzz_bit == "TEST_MODE") else "1'b0") + ";\n" 188 189 return v; 190 191def make_vlog(fuzz_bit): 192 vlog = code_prefix 193 vlog += inst_pll(fuzz_bit) 194 vlog += "endmodule" 195 return vlog 196 197known_bits = [] 198 199# Set to true to continue even if multiple bits are changed (needed because 200# of the issue discusssed below) 201show_all_bits = False #TODO: make this an argument 202 203device = "up5k" #TODO: environment variable? 204 205#HACK: icecube doesn't let you set all of the DIVQ bits to 0, 206#which makes fuzzing early on annoying as there is never a case 207#with just 1 bit set. So a tiny bit of semi-manual work is needed 208#first to discover this (basically run this script with show_all_bits=True 209#and look for the stuck bit) 210#TODO: clever code could get rid of this 211divq_bit0 = { 212 "up5k" : (11, 31, 3), 213 "lm4k" : (11, 0, 3) 214} 215 216#Return a list of PLL config bits in the format (x, y, bit) 217def parse_exp(expfile): 218 current_x = 0 219 current_y = 0 220 bits = [] 221 with open(expfile, 'r') as f: 222 for line in f: 223 splitline = line.split(' ') 224 if splitline[0] == ".io_tile": 225 current_x = int(splitline[1]) 226 current_y = int(splitline[2]) 227 elif splitline[0] == "PLL": 228 if splitline[1][:10] == "PLLCONFIG_": 229 bitidx = int(splitline[1][10:]) 230 bits.append((current_x, current_y, bitidx)) 231 return bits 232 233#Convert a bit tuple as returned from the above to a nice string 234def bit_to_str(bit): 235 return "(%d, %d, \"PLLCONFIG_%d\")" % bit 236 237#The main fuzzing function 238def do_fuzz(): 239 if not os.path.exists("./work_pllauto"): 240 os.mkdir("./work_pllauto") 241 known_bits.append(divq_bit0[device]) 242 with open("pll_data_" + device + ".txt", 'w') as dat: 243 for fuzz_bit in fuzz_bits: 244 vlog = make_vlog(fuzz_bit) 245 with open("./work_pllauto/pllauto.v", 'w') as f: 246 f.write(vlog) 247 retval = os.system("bash ../../icecube.sh -" + device + " ./work_pllauto/pllauto.v > ./work_pllauto/icecube.log 2>&1") 248 if retval != 0: 249 sys.stderr.write('ERROR: icecube returned non-zero error code\n') 250 sys.exit(1) 251 retval = os.system("../../../icebox/icebox_explain.py ./work_pllauto/pllauto.asc > ./work_pllauto/pllauto.exp") 252 if retval != 0: 253 sys.stderr.write('ERROR: icebox_explain returned non-zero error code\n') 254 sys.exit(1) 255 pll_bits = parse_exp("./work_pllauto/pllauto.exp") 256 new_bits = [] 257 for set_bit in pll_bits: 258 if not (set_bit in known_bits): 259 new_bits.append(set_bit) 260 if len(new_bits) == 0: 261 sys.stderr.write('ERROR: no new bits set when setting config bit ' + fuzz_bit + '\n') 262 sys.exit(1) 263 if len(new_bits) > 1: 264 sys.stderr.write('ERROR: multiple new bits set when setting config bit ' + fuzz_bit + '\n') 265 for bit in new_bits: 266 sys.stderr.write('\t' + bit_to_str(bit) + '\n') 267 if not show_all_bits: 268 sys.exit(1) 269 if len(new_bits) == 1: 270 known_bits.append(new_bits[0]) 271 #print DIVQ_0 at the right moment, as it's not fuzzed normally 272 if fuzz_bit == "DIVQ_1": 273 print(("\"DIVQ_0\":").ljust(24) + bit_to_str(divq_bit0[device]) + ",") 274 dat.write(("\"DIVQ_0\":").ljust(24) + bit_to_str(divq_bit0[device]) + ",\n") 275 print(("\"" + fuzz_bit + "\":").ljust(24) + bit_to_str(new_bits[0]) + ",") 276 dat.write(("\"" + fuzz_bit + "\":").ljust(24) + bit_to_str(new_bits[0]) + ",\n") 277do_fuzz()