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):
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,
dq_delay_taps = 0,
sclk_name = "SCLK_ODDR",
@ -507,9 +457,9 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
])
self.wdata = CSRStorage(description="Page data to write to FLASH",
fields = [
CSRField("wdata", size=16, description="""16-bit wide write data presented to FLASH, committed to a 128-entry deep FIFO.
Writes to this register are not cached; note that writes to the SPINOR address space are also committed
to the FIFO, but this space is cached by the CPU, and therefore not guaranteed to be coherent or in order.
CSRField("wdata", size=16, description="""16-bit wide write data presented to FLASH, committed to a 128-entry deep FIFO.
Writes to this register are not cached; note that writes to the SPINOR address space are also committed
to the FIFO, but this space is cached by the CPU, and therefore not guaranteed to be coherent or in order.
The direct wishbone-write address space is provisioned for e.g. USB bus masters that don't have caching.""")
]
)
@ -1468,3 +1418,53 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
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)