mirror of https://github.com/YosysHQ/icestorm.git
5k RGB driver reverse engineered
This commit is contained in:
parent
2f962ac92e
commit
25ad7a24b9
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
work_rgba_drv/
|
||||
|
|
@ -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()
|
||||
|
|
@ -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"),
|
||||
Loading…
Reference in New Issue