Skip to content

Instantly share code, notes, and snippets.

@raldone01
Created November 15, 2024 15:34
Show Gist options
  • Save raldone01/18c068c5410f38c7fd41c7abe1c275b8 to your computer and use it in GitHub Desktop.
Save raldone01/18c068c5410f38c7fd41c7abe1c275b8 to your computer and use it in GitHub Desktop.
STM32 FMC Timings calculator
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