soc/cores/spi_opi: move add_timing_constraints after __init__ to ease readability (first describe the logic then add the constraints).

This commit is contained in:
Florent Kermarrec 2020-11-09 11:15:00 +01:00
parent 5587ee5eea
commit 0627c01d89
1 changed files with 53 additions and 53 deletions

View File

@ -14,56 +14,6 @@ from litex.soc.integration.doc import AutoDoc, ModuleDoc
class S7SPIOPI(Module, AutoCSR, AutoDoc): class S7SPIOPI(Module, AutoCSR, AutoDoc):
def add_timing_constraints(self, platform, padgroup_name):
# reminder to self: the {{ and }} overloading is because Python treats these as special in strings, so {{ -> { in actual constraint
# NOTE: ECSn is deliberately not constrained -- it's more or less async (0-10ns delay on the signal, only meant to line up with "block" region
# constrain DQS-to-DQ input DDR delays
platform.add_platform_command("create_clock -name spidqs -period 10 [get_ports {}_dqs]".format(padgroup_name))
platform.add_platform_command("set_input_delay -clock spidqs -max 0.6 [get_ports {{" + padgroup_name + "_dq[*]}}]")
platform.add_platform_command("set_input_delay -clock spidqs -min 4.4 [get_ports {{" + padgroup_name + "_dq[*]}}]")
platform.add_platform_command(
"set_input_delay -clock spidqs -max 0.6 [get_ports {{" + padgroup_name + "_dq[*]}}] -clock_fall -add_delay")
platform.add_platform_command(
"set_input_delay -clock spidqs -min 4.4 [get_ports {{" + padgroup_name + "_dq[*]}}] -clock_fall -add_delay")
# derive clock for SCLK - clock-forwarded from DDR see Xilinx answer 62488 use case #4
platform.add_platform_command(
"create_generated_clock -name spiclk_out -multiply_by 1 -source [get_pins {}/Q] [get_ports {}_sclk]".format(
self.sclk_name, padgroup_name))
# constrain CIPO SDR delay -- WARNING: -max is 'actually' 5.0ns, but design can't meet timing @ 5.0 tPD from SPIROM. There is some margin in the timing closure tho, so 4.5ns is probably going to work....
platform.add_platform_command(
"set_input_delay -clock [get_clocks spiclk_out] -clock_fall -max 4.5 [get_ports {}_dq[1]]".format(padgroup_name))
platform.add_platform_command(
"set_input_delay -clock [get_clocks spiclk_out] -clock_fall -min 1 [get_ports {}_dq[1]]".format(padgroup_name))
# corresponding false path on CIPO DDR input when clocking SDR data
platform.add_platform_command(
"set_false_path -from [get_clocks spiclk_out] -to [get_pin {}/D ]".format(self.iddr_name + "1"))
# corresponding false path on CIPO SDR input from DQS strobe, only if the cipo path is used
if self.spiread:
platform.add_platform_command(
"set_false_path -from [get_clocks spidqs] -to [get_pin {}/D ]".format(self.cipo_name))
# constrain CLK-to-DQ output DDR delays; copi uses the same rules
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -max 1 [get_ports {{" + padgroup_name + "_dq[*]}}]")
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -min -1 [get_ports {{" + padgroup_name + "_dq[*]}}]")
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -max 1 [get_ports {{" + padgroup_name + "_dq[*]}}] -clock_fall -add_delay")
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -min -1 [get_ports {{" + padgroup_name + "_dq[*]}}] -clock_fall -add_delay")
# constrain CLK-to-CS output delay. NOTE: timings require one dummy cycle insertion between CS and SCLK (de)activations. Not possible to meet timing for DQ & single-cycle CS due to longer tS/tH reqs for CS
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -min -1 [get_ports {}_cs_n]".format(padgroup_name)) # -3 in reality
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -max 1 [get_ports {}_cs_n]".format(padgroup_name)) # 4.5 in reality
# unconstrain OE path - we have like 10+ dummy cycles to turn the bus on wr->rd, and 2+ cycles to turn on end of read
platform.add_platform_command("set_false_path -through [ get_pins {net}_reg/Q ]", net=self.dq.oe)
platform.add_platform_command("set_false_path -through [ get_pins {net}_reg/Q ]",
net=self.dq_copi.oe)
def __init__(self, pads, def __init__(self, pads,
dq_delay_taps = 0, dq_delay_taps = 0,
sclk_name = "SCLK_ODDR", sclk_name = "SCLK_ODDR",
@ -1468,3 +1418,53 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
ecc_reported.eq(0) ecc_reported.eq(0)
) )
] ]
def add_timing_constraints(self, platform, padgroup_name):
# reminder to self: the {{ and }} overloading is because Python treats these as special in strings, so {{ -> { in actual constraint
# NOTE: ECSn is deliberately not constrained -- it's more or less async (0-10ns delay on the signal, only meant to line up with "block" region
# constrain DQS-to-DQ input DDR delays
platform.add_platform_command("create_clock -name spidqs -period 10 [get_ports {}_dqs]".format(padgroup_name))
platform.add_platform_command("set_input_delay -clock spidqs -max 0.6 [get_ports {{" + padgroup_name + "_dq[*]}}]")
platform.add_platform_command("set_input_delay -clock spidqs -min 4.4 [get_ports {{" + padgroup_name + "_dq[*]}}]")
platform.add_platform_command(
"set_input_delay -clock spidqs -max 0.6 [get_ports {{" + padgroup_name + "_dq[*]}}] -clock_fall -add_delay")
platform.add_platform_command(
"set_input_delay -clock spidqs -min 4.4 [get_ports {{" + padgroup_name + "_dq[*]}}] -clock_fall -add_delay")
# derive clock for SCLK - clock-forwarded from DDR see Xilinx answer 62488 use case #4
platform.add_platform_command(
"create_generated_clock -name spiclk_out -multiply_by 1 -source [get_pins {}/Q] [get_ports {}_sclk]".format(
self.sclk_name, padgroup_name))
# constrain CIPO SDR delay -- WARNING: -max is 'actually' 5.0ns, but design can't meet timing @ 5.0 tPD from SPIROM. There is some margin in the timing closure tho, so 4.5ns is probably going to work....
platform.add_platform_command(
"set_input_delay -clock [get_clocks spiclk_out] -clock_fall -max 4.5 [get_ports {}_dq[1]]".format(padgroup_name))
platform.add_platform_command(
"set_input_delay -clock [get_clocks spiclk_out] -clock_fall -min 1 [get_ports {}_dq[1]]".format(padgroup_name))
# corresponding false path on CIPO DDR input when clocking SDR data
platform.add_platform_command(
"set_false_path -from [get_clocks spiclk_out] -to [get_pin {}/D ]".format(self.iddr_name + "1"))
# corresponding false path on CIPO SDR input from DQS strobe, only if the cipo path is used
if self.spiread:
platform.add_platform_command(
"set_false_path -from [get_clocks spidqs] -to [get_pin {}/D ]".format(self.cipo_name))
# constrain CLK-to-DQ output DDR delays; copi uses the same rules
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -max 1 [get_ports {{" + padgroup_name + "_dq[*]}}]")
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -min -1 [get_ports {{" + padgroup_name + "_dq[*]}}]")
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -max 1 [get_ports {{" + padgroup_name + "_dq[*]}}] -clock_fall -add_delay")
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -min -1 [get_ports {{" + padgroup_name + "_dq[*]}}] -clock_fall -add_delay")
# constrain CLK-to-CS output delay. NOTE: timings require one dummy cycle insertion between CS and SCLK (de)activations. Not possible to meet timing for DQ & single-cycle CS due to longer tS/tH reqs for CS
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -min -1 [get_ports {}_cs_n]".format(padgroup_name)) # -3 in reality
platform.add_platform_command(
"set_output_delay -clock [get_clocks spiclk_out] -max 1 [get_ports {}_cs_n]".format(padgroup_name)) # 4.5 in reality
# unconstrain OE path - we have like 10+ dummy cycles to turn the bus on wr->rd, and 2+ cycles to turn on end of read
platform.add_platform_command("set_false_path -through [ get_pins {net}_reg/Q ]", net=self.dq.oe)
platform.add_platform_command("set_false_path -through [ get_pins {net}_reg/Q ]",
net=self.dq_copi.oe)