Created
November 15, 2024 15:34
-
-
Save raldone01/18c068c5410f38c7fd41c7abe1c275b8 to your computer and use it in GitHub Desktop.
STM32 FMC Timings calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from time import perf_counter_ns | |
# Derived from https://github.com/godunko/scd40-sandbox/blob/893654c479f64c9078e839a6242d53695bddb9d3/documentation/NOTES.md?plain=1#L8 | |
# Thank you for doing the hard work, godunko! | |
# Clock definitions | |
HCLK = 216_000_000 # 216 MHz in Hz | |
FMCCLK = HCLK / 2 # FMC clock derived from HCLK | |
T_FMCCLK = 1 / FMCCLK # FMC clock period in seconds | |
T_FMCCLK_ns = T_FMCCLK * 1e9 # FMC clock period in nanoseconds | |
# Timing constraints for the LCD NT35510 | |
# If you have a different LCD, you may need to adjust these values | |
TRDLFM_MIN = 150 # in ns | |
TRDHFM_MIN = 250 # in ns | |
TRDCFM_MIN = 400 # in ns | |
TWRL_MIN = 15 # in ns | |
TANT_MIN = 2 # in ns | |
TWRH_MIN = 15 # in ns | |
TWC_MIN = 33 # in ns | |
# Function to find the optimal timing configuration | |
def find_optimal_timing(): | |
optimal_timing_r = None | |
optimal_sum_r = float("inf") # Initialize with a large value | |
# Iterate over possible values for address_setup_time, data_setup_time, and bus_turn_around_time | |
for bus_turn_around_time_r in range(16): | |
for address_setup_time_r in range(16): | |
for data_setup_time_r in range(1, 256): # data_setup_time starts from 1 | |
# Reading Timings | |
trdlfm = data_setup_time_r * T_FMCCLK_ns | |
trdhfm = ( | |
address_setup_time_r + (bus_turn_around_time_r + 1) | |
) * T_FMCCLK_ns | |
trdcfm = trdlfm + trdhfm | |
if ( | |
trdlfm >= TRDLFM_MIN | |
and trdhfm >= TRDHFM_MIN | |
and trdcfm >= TRDCFM_MIN | |
): | |
total_sum = ( | |
address_setup_time_r | |
+ data_setup_time_r | |
+ bus_turn_around_time_r | |
) | |
if total_sum < optimal_sum_r: | |
optimal_sum_r = total_sum | |
optimal_timing_r = { | |
"type": "READ", | |
"address_setup_time": address_setup_time_r, | |
"data_setup_time": data_setup_time_r, | |
"bus_turn_around_time": bus_turn_around_time_r, | |
"trdlfm": trdlfm, | |
"trdhfm": trdhfm, | |
"trdcfm": trdcfm, | |
"total_sum": total_sum, | |
} | |
optimal_timing_w = None | |
optimal_sum_w = float("inf") # Initialize with a large value | |
for bus_turn_around_time_w in range(16): | |
for address_setup_time_w in range(16): | |
for data_setup_time_w in range(1, 256): # data_setup_time starts from 1 | |
# Writing Timings | |
twrl = data_setup_time_w * T_FMCCLK_ns | |
tant = 1 | |
twrh = ( | |
address_setup_time_w + (bus_turn_around_time_w + 1) | |
) * T_FMCCLK_ns | |
twc = twrl + twrh | |
if ( | |
twrl >= TWRL_MIN | |
and tant * T_FMCCLK_ns >= TANT_MIN | |
and twrh >= TWRH_MIN | |
and twc >= TWC_MIN | |
): | |
total_sum = ( | |
address_setup_time_w | |
+ data_setup_time_w | |
+ bus_turn_around_time_w | |
) | |
if total_sum < optimal_sum_w: | |
optimal_sum_w = total_sum | |
optimal_timing_w = { | |
"type": "WRITE", | |
"address_setup_time": address_setup_time_w, | |
"data_setup_time": data_setup_time_w, | |
"bus_turn_around_time": bus_turn_around_time_w, | |
"twrl": twrl, | |
"twrh": twrh, | |
"twc": twc, | |
"total_sum": total_sum, | |
} | |
return optimal_timing_r, optimal_timing_w | |
def pretty_print_timing(timing): | |
def clocks_to_str(clocks): | |
return f"{clocks} clocks, {round(clocks * T_FMCCLK_ns, 1)} ns" | |
print(f"Type: {timing['type']}") | |
print(f"Address Setup Time: {clocks_to_str(timing['address_setup_time'])}") | |
print(f"Data Setup Time: {clocks_to_str(timing['data_setup_time'])}") | |
print(f"Bus Turn Around Time: {clocks_to_str(timing['bus_turn_around_time'])}") | |
print(f"Total Sum: {clocks_to_str(timing['total_sum'])}") | |
print() | |
def ns_and_margins_to_str(ns, min_ns): | |
return f"{round(ns, 1)} ns >= {min_ns} ns, margin: {round(ns - min_ns, 1)} ns" | |
# print the tolerances and margins | |
if timing["type"] == "READ": | |
print("Read Timings:") | |
print(f"TRDLFM: {ns_and_margins_to_str(timing['trdlfm'], TRDLFM_MIN)}") | |
print(f"TRDHFM: {ns_and_margins_to_str(timing['trdhfm'], TRDHFM_MIN)}") | |
print(f"TRDCFM: {ns_and_margins_to_str(timing['trdcfm'], TRDCFM_MIN)}") | |
elif timing["type"] == "WRITE": | |
print("Write Timings:") | |
print(f"TWRL: {ns_and_margins_to_str(timing['twrl'], TWRL_MIN)}") | |
print(f"TANT: {ns_and_margins_to_str(1 * T_FMCCLK_ns, TANT_MIN)}") | |
print(f"TWRH: {ns_and_margins_to_str(timing['twrh'], TWRH_MIN)}") | |
print(f"TWC: {ns_and_margins_to_str(timing['twc'], TWC_MIN)}") | |
def format_time_ns(elapsed_ns: int): | |
""" | |
Helper function to format the elapsed time in the appropriate unit | |
(seconds, milliseconds, microseconds, or nanoseconds). | |
""" | |
if elapsed_ns >= 1_000_000_000: # More than or equal to 1 second | |
scale = 1_000_000_000 | |
elapsed = elapsed_ns / scale | |
unit = "s" | |
elif elapsed_ns >= 1_000_000: # More than or equal to 1 millisecond | |
scale = 1_000_000 | |
elapsed = elapsed_ns / scale | |
unit = "ms" | |
elif elapsed_ns >= 1_000: # More than or equal to 1 microsecond | |
scale = 1_000 | |
elapsed = elapsed_ns / scale | |
unit = "µs" | |
else: | |
scale = 1 | |
elapsed = elapsed_ns | |
unit = "ns" | |
return elapsed, unit, scale | |
# Run the function and display the optimal result | |
if __name__ == "__main__": | |
print( | |
f"Finding optimal timing configuration for FMC LCD NT35510 and a clock of {HCLK / 1e6} MHz" | |
) | |
calc_start = perf_counter_ns() | |
optimal_result_r, optimal_result_w = find_optimal_timing() | |
calc_end = perf_counter_ns() | |
calc_elapsed_ns = calc_end - calc_start | |
calc_elapsed, unit, scale = format_time_ns(calc_elapsed_ns) | |
print(f"Calculation took {calc_elapsed:.2f} {unit}") | |
print() | |
print("Optimal Read Timing Configuration:") | |
pretty_print_timing(optimal_result_r) | |
print() | |
print("Optimal Write Timing Configuration:") | |
pretty_print_timing(optimal_result_w) | |
print() | |
print("Thank you for using me!") | |
print("Yours truly, the one and only FMC LCD NT35510 Timing Calculator!") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment