5k RGB driver reverse engineered

This commit is contained in:
David Shah 2017-11-14 14:06:38 +00:00
parent 2f962ac92e
commit 25ad7a24b9
4 changed files with 223 additions and 0 deletions

View File

@ -217,4 +217,32 @@ The <span style="font-family:monospace">CLKLF</span> output of SB_LFOSC is conne
<tr><td><em>SPRAM_ENABLE</em></td><td><em>(0, 1, CBIT_0)</em></td><td><em>(0, 1, CBIT_1)</em></td><td><em>(25, 1, CBIT_0)</em></td><td><em>(25, 1, CBIT_1)</em></td></tr>
</table>
<h2>RGB LED Driver</h2>
<p>The UltraPlus devices contain an internal 3-channel 2-24mA constant-current driver intended for RGB led driving (SB_RGBA_DRV). It is broken out onto 3 pins: 39, 40 and 41 on the QFN48 package.
The LED driver is implemented using the IPConnect tiles and is entirely seperate to the IO cells, if the LED driver is ignored or disabled on a pin then the pin
can be used as an open-drain IO using the standard IO cell.</p>
<p>Note that the UltraPlus devices also have a seperate PWM generator IP core, which would often be connected to this one to create LED effects such as "breathing" without
involving FPGA resources.</p>
<p>The LED driver connections are shown in the label below.</p>
<table class="ctab">
<tr><th>Signal</th><th>Net</th></tr>
<tr><td>CURREN</td><td>(25, 29, lutff_6/in_3)</td></tr>
<tr><td>RGBLEDEN</td><td>(0, 30, lutff_1/in_1)</td></tr>
<tr><td>RGB0PWM</td><td>(0, 30, lutff_2/in_1)</td></tr>
<tr><td>RGB1PWM</td><td>(0, 30, lutff_3/in_1)</td></tr>
<tr><td>RGB2PWM</td><td>(0, 30, lutff_4/in_1)</td></tr>
</table>
<p>The configuration bits are as follows. As well as the documented bits, another bit "RGBA_DRV_EN" is set if any of the channels are enabled.</p>
<table class="ctab">
<tr><th>Parameter</th><th>Bit</th></tr>
<tr><td>RGBA_DRV_EN</td><td>(0, 28, CBIT_5)</td></tr>
<tr><td>RGB0_CURRENT[1:0]</td><td>(0, 28, CBIT_[7:6])</td></tr>
<tr><td>RGB0_CURRENT[5:2]</td><td>(0, 29, CBIT_[3:0])</td></tr>
<tr><td>RGB1_CURRENT[3:0]</td><td>(0, 29, CBIT_[7:4])</td></tr>
<tr><td>RGB1_CURRENT[5:4]</td><td>(0, 30, CBIT_[1:0])</td></tr>
<tr><td>RGB2_CURRENT[5:0]</td><td>(0, 30, CBIT_[7:2])</td></tr>
<tr><td>CURRENT_MODE</td><td>(0, 28, CBIT_4)</td></tr>
</table>
</body></html>

View File

@ -0,0 +1 @@
work_rgba_drv/

View File

@ -0,0 +1,174 @@
#!/usr/bin/env python3
import os, sys
# SB_RGBA_DRV automatic fuzzing script
device = "up5k"
# SB_RGBA_DRV 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
fuzz_bits = [
"RGB0_CURRENT_0",
"RGB0_CURRENT_1",
"RGB0_CURRENT_2",
"RGB0_CURRENT_3",
"RGB0_CURRENT_4",
"RGB0_CURRENT_5",
"RGB1_CURRENT_0",
"RGB1_CURRENT_1",
"RGB1_CURRENT_2",
"RGB1_CURRENT_3",
"RGB1_CURRENT_4",
"RGB1_CURRENT_5",
"RGB2_CURRENT_0",
"RGB2_CURRENT_1",
"RGB2_CURRENT_2",
"RGB2_CURRENT_3",
"RGB2_CURRENT_4",
"RGB2_CURRENT_5",
"CURRENT_MODE"
]
# Boilerplate code based on the icefuzz script
code_prefix = """
module top(
input curren,
input rgbleden,
input r_in,
input g_in,
input b_in,
output r_led,
output g_led,
output b_led);
"""
def get_param_value(param_name, param_size, fuzz_bit):
param = "\"0b";
#In the RGB driver, once bit i of a current parameter is set i-1..0 must also be set
is_high = False
for i in range(param_size - 1, -1, -1):
if fuzz_bit == param_name + "_" + str(i) or (i == 0 and fuzz_bit == "CURRENT_MODE") or is_high:
param += '1'
is_high = True
else:
param += '0'
param += "\""
return param
def inst_rgba(fuzz_bit):
v = ""
v += """SB_RGBA_DRV rgba_inst (
.CURREN(curren),
.RGBLEDEN(rgbleden),
.RGB0PWM(r_in),
.RGB1PWM(g_in),
.RGB2PWM(b_in),
.RGB0(r_led),
.RGB1(g_led),
.RGB2(b_led)
);"""
v += "defparam rgba_inst.CURRENT_MODE = " + get_param_value("CURRENT_MODE", 1, fuzz_bit) + ";\n"
v += "defparam rgba_inst.RGB0_CURRENT = " + get_param_value("RGB0_CURRENT", 6, fuzz_bit) + ";\n"
v += "defparam rgba_inst.RGB1_CURRENT = " + get_param_value("RGB1_CURRENT", 6, fuzz_bit) + ";\n"
v += "defparam rgba_inst.RGB2_CURRENT = " + get_param_value("RGB2_CURRENT", 6, fuzz_bit) + ";\n"
return v;
def make_vlog(fuzz_bit):
vlog = code_prefix
vlog += inst_rgba(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 config 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
rgba_drv_en_bit = {
"up5k" : (0, 28, 5)
}
#Return a list of RGBA_DRIVER 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].endswith("_tile"):
current_x = int(splitline[1])
current_y = int(splitline[2])
elif splitline[0] == "IpConfig":
if splitline[1][:5] == "CBIT_":
bitidx = int(splitline[1][5:])
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, \"CBIT_%d\")" % bit
#The main fuzzing function
def do_fuzz():
if not os.path.exists("./work_rgba_drv"):
os.mkdir("./work_rgba_drv")
known_bits.append(rgba_drv_en_bit[device])
with open("rgba_drv_data_" + device + ".txt", 'w') as dat:
for fuzz_bit in fuzz_bits:
vlog = make_vlog(fuzz_bit)
with open("./work_rgba_drv/rgba_drv.v", 'w') as f:
f.write(vlog)
with open("./work_rgba_drv/rgba_drv.pcf", 'w') as f:
f.write("""
set_io r_led 39
set_io g_led 40
set_io b_led 41
""")
retval = os.system("bash ../../icecube.sh -" + device + " ./work_rgba_drv/rgba_drv.v > ./work_rgba_drv/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_rgba_drv/rgba_drv.asc > ./work_rgba_drv/rgba_drv.exp")
if retval != 0:
sys.stderr.write('ERROR: icebox_explain returned non-zero error code\n')
sys.exit(1)
rgba_bits = parse_exp("./work_rgba_drv/rgba_drv.exp")
new_bits = []
for set_bit in rgba_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 == "RGB0_CURRENT_0":
print(("\"RGBA_DRV_EN\":").ljust(24) + bit_to_str(rgba_drv_en_bit[device]) + ",")
dat.write(("\"RGBA_DRV_EN\":").ljust(24) + bit_to_str(rgba_drv_en_bit[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()

View File

@ -0,0 +1,20 @@
"RGBA_DRV_EN": (0, 28, "CBIT_5"),
"RGB0_CURRENT_0": (0, 28, "CBIT_6"),
"RGB0_CURRENT_1": (0, 28, "CBIT_7"),
"RGB0_CURRENT_2": (0, 29, "CBIT_0"),
"RGB0_CURRENT_3": (0, 29, "CBIT_1"),
"RGB0_CURRENT_4": (0, 29, "CBIT_2"),
"RGB0_CURRENT_5": (0, 29, "CBIT_3"),
"RGB1_CURRENT_0": (0, 29, "CBIT_4"),
"RGB1_CURRENT_1": (0, 29, "CBIT_5"),
"RGB1_CURRENT_2": (0, 29, "CBIT_6"),
"RGB1_CURRENT_3": (0, 29, "CBIT_7"),
"RGB1_CURRENT_4": (0, 30, "CBIT_0"),
"RGB1_CURRENT_5": (0, 30, "CBIT_1"),
"RGB2_CURRENT_0": (0, 30, "CBIT_2"),
"RGB2_CURRENT_1": (0, 30, "CBIT_3"),
"RGB2_CURRENT_2": (0, 30, "CBIT_4"),
"RGB2_CURRENT_3": (0, 30, "CBIT_5"),
"RGB2_CURRENT_4": (0, 30, "CBIT_6"),
"RGB2_CURRENT_5": (0, 30, "CBIT_7"),
"CURRENT_MODE": (0, 28, "CBIT_4"),