FPGARelated.com
Forums

How to define multi-cycle timing constraints in Lattice iCEcube2 (synplify)

Started by Stef July 11, 2023
In a design, I have a number of counters that have an enable that is
only active every N periods of the clock signal. With only a clock of
200MHz in the timing constraints, the counters are assumed to run at
200MHz and timing fails. As these counters actually run at much lower
speed, I know they will work. But how to convince the tools (Lattice
iCEcube2) that all is OK?

I am trying to add multi-cycle constraints, but cannot get them to work.
The standard iCEcube timing constraints editor can only create
multi-cycle from input pin to output pin, and that does not even work. I
need to add a multi-cycle constraint on an internal counter. Preferrably
with some wildcard, as I have an array of counters, each with a number
of bits. Specifying each separtely would require a lot of lines.

One example of a failed path (from the iCEcube timing analyzer)
  Start            protection_inst.filter_array_3_filter_inst.filter_count_1_LC_5_5_1/lcout
  End              protection_inst.filter_array_3_filter_inst.filter_count_5_LC_5_5_5/in3
  Reference        gen_pll_wc_lattice_pll_inst.wc_lattice_pll_inst_pll/PLLOUTCORE
  Setup Constraint 5262(p)
  Path Slack       -762(p)

Simply adding this does not work:
  set_multicycle_path  -to [get_cells {protection_inst.filter_array_3_filter_inst.filter_count_5_LC_5_5_5/in3}] 8

Synplify output:
protection_inst.filter_array_3_filter_inst.filter_count_5_LC_5_5_5/in3])
(multi path 8) was not applied to the design because none of the '-to'
objects specified by the constraint exist in the design

I've tried get_nets and get_pins instead of get_cells, same result. Also
tried other parts of the instance names with wildcards, nothing found.

How to specify multi-cycle paths for the feedback of these internal
counters?


-- 
Stef

He who has the courage to laugh is almost as much a master of the world
as he who is ready to die.
		-- Giacomo Leopardi
On 2023-07-11 Stef wrote in comp.arch.fpga:

After some rewriting of the counter code, they now meet timing when
running at 200MHz. So all timing even single cycle okay, no immediate
need for multi-cycle constraints.

But the question remains valid: how to define multi-cycle constraints?
It is a bit absurd to require passing timing for a 200MHz clock when
some of these counters only run at a few kHz.

-- 
Stef

50% of the manual is in .pdf readme files
On Thursday, July 13, 2023 at 7:18:37 AM UTC-4, Stef wrote:
> On 2023-07-11 Stef wrote in comp.arch.fpga: > > But the question remains valid: how to define multi-cycle constraints? > It is a bit absurd to require passing timing for a 200MHz clock when > some of these counters only run at a few kHz. >
I haven't used Lattice tools, so I don't know the answer to your question. However, your complaint "...a bit absurd to require passing timing for a 200MHz clock when some of these counters only run at a few kHz" is very wrong. Every bit in a counter will depend on all of the lower/less significant bits. In other words, bits 7-1 will depend on bit 0. Bit 0 will be toggling at a 200 MHz rate so the logic for computing bit 7 will have to operate at that speed as well. 5 ns after the counter is at 11111111 all bits will be flipping to 00000000. To see these logic paths, just take a look at the post-fit logic and you'll see that what I described is correct. So the entire premise of your question is not correct. Even if you do find out how to add multi-cycle constraints in your tool, you'll want to be careful. The tools don't know that the constraint you entered is not correct which means it won't report a valid timing problem because of this error. You could end up scratching your head trying to figure out why things are acting flaky (i.e. an actual timing problem) when the tool says that the timing is correct. As far as I know, there is no 'error checking' to see that user supplied constraints are in fact correct. Kevin Jennings
On 7/14/23 6:30 AM, KJ wrote:
> On Thursday, July 13, 2023 at 7:18:37 AM UTC-4, Stef wrote: >> On 2023-07-11 Stef wrote in comp.arch.fpga: >> >> But the question remains valid: how to define multi-cycle constraints? >> It is a bit absurd to require passing timing for a 200MHz clock when >> some of these counters only run at a few kHz. >> > I haven't used Lattice tools, so I don't know the answer to your question. However, your complaint "...a bit absurd to require passing timing for a 200MHz clock when some of these counters only run at a few kHz" is very wrong. Every bit in a counter will depend on all of the lower/less significant bits. In other words, bits 7-1 will depend on bit 0. Bit 0 will be toggling at a 200 MHz rate so the logic for computing bit 7 will have to operate at that speed as well. 5 ns after the counter is at 11111111 all bits will be flipping to 00000000. To see these logic paths, just take a look at the post-fit logic and you'll see that what I described is correct. > > So the entire premise of your question is not correct. Even if you do find out how to add multi-cycle constraints in your tool, you'll want to be careful. The tools don't know that the constraint you entered is not correct which means it won't report a valid timing problem because of this error. You could end up scratching your head trying to figure out why things are acting flaky (i.e. an actual timing problem) when the tool says that the timing is correct. As far as I know, there is no 'error checking' to see that user supplied constraints are in fact correct. > > Kevin Jennings
The OP said that it was a counter with enable, and the enable only occured every N cycles, so your statement isn't true. The enable -> counter-ff needs to meet the requirement, but that signal will tend to feed the last part of the logic, so shouldn't be a problem, the timing limitation being the carry chain from a low bit to a high bit.
On Friday, July 14, 2023 at 7:23:17 AM UTC-4, Richard Damon wrote:
> On 7/14/23 6:30 AM, KJ wrote:
> The OP said that it was a counter with enable, and the enable only > occured every N cycles, so your statement isn't true. > > The enable -> counter-ff needs to meet the requirement, but that signal > will tend to feed the last part of the logic, so shouldn't be a problem, > the timing limitation being the carry chain from a low bit to a high bit.
What I said in the earlier post is accurate, but you are making assumptions about the enable signal logic path that might not be accurate. The enable signal could be just another logic signal that feeds into logic that generates the D input of the flip flops of the counter. Or, IF the part has a D-FF primitive with a dedicated Clock Enable input, AND that clock enable input is actually used by the tool then the enable-->counter FF path would be separate from the D input logic. There might be tool specific, non-portable ways to implement this but you're still going to have to depend on the tool to do what you think is right. If the enable signal is just another input to the logic that feeds into the D input of the flip flops of the counter then there are no guarantees that the signal will 'tend to feed the last part of the logic' as you mentioned (although it might). Since the OP said he rewrote the counter logic and timing now works, presumably he simplified the logic that went into determining when the counter should increment. A simple way to accomplish this is to make sure there is an actual discrete enable signal by syncing to the clock before using that signal to enable the counter. In any case, this doesn't address the OP's question about how to specify multi-cycle constraints in the Lattice tools so this is all a tangent but good discussion. Kevin Jennings
On 2023-07-14 KJ wrote in comp.arch.fpga:
> On Friday, July 14, 2023 at 7:23:17 AM UTC-4, Richard Damon wrote: >> On 7/14/23 6:30 AM, KJ wrote: > >> The OP said that it was a counter with enable, and the enable only >> occured every N cycles, so your statement isn't true. >> >> The enable -> counter-ff needs to meet the requirement, but that signal >> will tend to feed the last part of the logic, so shouldn't be a problem, >> the timing limitation being the carry chain from a low bit to a high bit. > > What I said in the earlier post is accurate, but you are making assumptions about the enable signal logic path that might not be accurate. The enable signal could be just another logic signal that feeds into logic that generates the D input of the flip flops of the counter. Or, IF the part has a D-FF primitive with a dedicated Clock Enable input, AND that clock enable input is actually used by the tool then the enable-->counter FF path would be separate from the D input logic. There might be tool specific, non-portable ways to implement this but you're still going to have to depend on the tool to do what you think is right. > > If the enable signal is just another input to the logic that feeds into the D input of the flip flops of the counter then there are no guarantees that the signal will 'tend to feed the last part of the logic' as you mentioned (although it might). > > Since the OP said he rewrote the counter logic and timing now works, presumably he simplified the logic that went into determining when the counter should increment. A simple way to accomplish this is to make sure there is an actual discrete enable signal by syncing to the clock before using that signal to enable the counter. >
Okay, you're both right. :-) In the counter design I had an enable signal for the clock and since the Lattice hardware has DFFs with an enable input, I _assumed_ this would be used. And as always: Assumption is the mother of all f*ckups. When examining the P&R result, I found the enable was included in the feedback logic and not as a direct enable signal to the DFF. :-( The counters need to count up and down and stop at the end points (0 and max). And there is an output signal that switches only at the end points. This was the original counter code: process(clk, reset) begin if reset = '1' then filter_count <= 0; sig_out <= '0'; elsif rising_edge(clk) and clk_enable = '1' then if sig_in = '1' then if filter_count < count_max then filter_count <= filter_count + 1; else sig_out <= '1'; end if; else if filter_count > 0 then filter_count <= filter_count - 1; else sig_out <= '0'; end if; end if; end if; end process; This resulted in logic with a DFF with no enable signal and the enable included in the counter feedback logic. Changing the counter part to this: if sig_in = '1' then if filter_count = count_max then sig_out <= '1'; else filter_count <= filter_count + 1; end if; else if filter_count = 0 then sig_out <= '0'; else filter_count <= filter_count - 1; end if; end if; Resulted in using DFFs with an enable signal, which connects to the clk_enable in the code. And as the enable is removed from the feedback logic, this logic is simpler and can now meet the single cycle 200MHz timing as an added bonus. This shows you indeed have to be very carefull when wanting to use multi-cycle constraints. Make sure the logic is indeed implemented with an actual clock enable on the DFF. But if this is the case and timing of the enable is okay (don't set multi-cycle on that one), this counter could probably handle an even faster clock. If only the tools understood the multi-cycle nature of the counter. And that brings us to this point:
> In any case, this doesn't address the OP's question about how to specify multi-cycle constraints in the Lattice tools so this is all a tangent but good discussion. >
The lattice tools use synplify, which is used by multiple vendors. So I would expect this syntax not to be vendor specific. Although the signal name mangling after synthesis may vary between vendors? Al my searches so far turned up only very simple examples like: set_multicycle_path -end -setup -to [get_cells regb] 2 Which I haven't been able to translate to something that will work in my design. And when I look at the code again, this is just as well. :-) The sig_in signal can change on every 200MHz clock cycle, so logic handling that signal should pass 200MHz timing. And this is in the counter feedback logic. For the counter to be truly multicycle, sig_in should be passed through a DFF with the same clock enable as well. So be very carefull if I ever figure out how to do multi-cycle constaints. Just keeping everything on max speed timing is at least a safe aproach. -- Stef According to all the latest reports, there was no truth in any of the earlier reports.
On 7/14/23 10:11 AM, KJ wrote:
> On Friday, July 14, 2023 at 7:23:17&#8239;AM UTC-4, Richard Damon wrote: >> On 7/14/23 6:30 AM, KJ wrote: > >> The OP said that it was a counter with enable, and the enable only >> occured every N cycles, so your statement isn't true. >> >> The enable -> counter-ff needs to meet the requirement, but that signal >> will tend to feed the last part of the logic, so shouldn't be a problem, >> the timing limitation being the carry chain from a low bit to a high bit. > > What I said in the earlier post is accurate, but you are making assumptions about the enable signal logic path that might not be accurate. The enable signal could be just another logic signal that feeds into logic that generates the D input of the flip flops of the counter. Or, IF the part has a D-FF primitive with a dedicated Clock Enable input, AND that clock enable input is actually used by the tool then the enable-->counter FF path would be separate from the D input logic. There might be tool specific, non-portable ways to implement this but you're still going to have to depend on the tool to do what you think is right. > > If the enable signal is just another input to the logic that feeds into the D input of the flip flops of the counter then there are no guarantees that the signal will 'tend to feed the last part of the logic' as you mentioned (although it might). > > Since the OP said he rewrote the counter logic and timing now works, presumably he simplified the logic that went into determining when the counter should increment. A simple way to accomplish this is to make sure there is an actual discrete enable signal by syncing to the clock before using that signal to enable the counter. > > In any case, this doesn't address the OP's question about how to specify multi-cycle constraints in the Lattice tools so this is all a tangent but good discussion. > > Kevin Jennings
Yes, it is possible to generate code that the enable signal doesn't end up on the fast path, but any compiler that does that without being forced into is very bad, as the natural equations would become a mux on the enable selecting between the current results and the incremented value. The one way that I can think of to put the enable on the slow path is to make the equations D <= Q + enable, and if that is how you wrote it, you deserve the slowness.
On Friday, July 14, 2023 at 7:56:37=E2=80=AFPM UTC-4, Richard Damon wrote:

> Yes, it is possible to generate code that the enable signal doesn't end=
=20
> up on the fast path, but any compiler that does that without being=20 > forced into is very bad, as the natural equations would become a mux on=
=20
> the enable selecting between the current results and the incremented valu=
e.=20
>=20 > The one way that I can think of to put the enable on the slow path is to=
=20
> make the equations D <=3D Q + enable, and if that is how you wrote it, yo=
u=20
> deserve the slowness.
Here's another way putting the enable on the slow path can happen...read th= e OP's last post. What he did to get the tool to use the clock enable is t= o change logic that was already inside the outer if statement looking at th= e clock enable. He changed "if filter_count < count_max" to "if filter_cou= nt =3D count_max" and "if filter_count > 0 then" to "if filter_count =3D 0 = then". Using equals instead of less than and greater than typically reduce= s the amount of logic that goes into the D input but will have no logical i= mpact on whether a clock enable input to a FF could be used. The outermost= "elsif rising_edge(clk) and clk_enable =3D '1' then" is unchanged between = his two approaches. By your measure, it seems Synplify used by Lattice is "very bad". @Stef Here are a couple of multicycle path statements from a working Quartus FPGA= design. The only notable difference I see from what you posted is the use= of get_keepers instead of get_cells as you had. If I recall correctly, Qu= artus uses Synopsys (or at least the same syntax). set_multicycle_path -setup -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]= }] 2 set_multicycle_path -hold -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}= ] 2 On another design, that uses an FPGA from Efinix, their tool Efinity is way= more limited. In that design you can only specify multi-cycle paths betwe= en clock domains but it doesn't look like you use get_* at all. set_multicycle_path -from Afe_AdcClk_ext -to Afe_ClockX4 -setup -end 2 Maybe give get_keepers a shot just to see if that is the syntax you need to= use to get rid of the error "...was not applied to the design because none= of the '-to' objects specified by the constraint exist in the design". Wh= ether or not using multi cycle is useful in this situation (which as you fo= und it was not), it is still useful to be able to use multicycle when it is= appropriate (like a single signal crossing clock domains). Kevin Jennings
On 7/15/23 9:45 AM, KJ wrote:
> On Friday, July 14, 2023 at 7:56:37&#8239;PM UTC-4, Richard Damon wrote: > >> Yes, it is possible to generate code that the enable signal doesn't end >> up on the fast path, but any compiler that does that without being >> forced into is very bad, as the natural equations would become a mux on >> the enable selecting between the current results and the incremented value. >> >> The one way that I can think of to put the enable on the slow path is to >> make the equations D <= Q + enable, and if that is how you wrote it, you >> deserve the slowness. > > Here's another way putting the enable on the slow path can happen...read the OP's last post. What he did to get the tool to use the clock enable is to change logic that was already inside the outer if statement looking at the clock enable. He changed "if filter_count < count_max" to "if filter_count = count_max" and "if filter_count > 0 then" to "if filter_count = 0 then". Using equals instead of less than and greater than typically reduces the amount of logic that goes into the D input but will have no logical impact on whether a clock enable input to a FF could be used. The outermost "elsif rising_edge(clk) and clk_enable = '1' then" is unchanged between his two approaches. > > By your measure, it seems Synplify used by Lattice is "very bad". >
No, his clk_enable will end up at the last block of logic for the flip-flop, (either using an actual enable pin, or the last LUT feeding the flip-flop. My comment was strictly about the timing of the ENABLE signal. The change improved the logic for the COUNTER part of the logic (since equal to a constant is much easier to compute than a less than comparison). Its possible that this simplification allowed the compiler to recognize that the enable signal was a simple enable and could use that operation of the Logic Element, and thus be faster (and allowing more counter logic in that last LUT of the Logic Element).
> @Stef > Here are a couple of multicycle path statements from a working Quartus FPGA design. The only notable difference I see from what you posted is the use of get_keepers instead of get_cells as you had. If I recall correctly, Quartus uses Synopsys (or at least the same syntax). > > set_multicycle_path -setup -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}] 2 > set_multicycle_path -hold -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}] 2 > > On another design, that uses an FPGA from Efinix, their tool Efinity is way more limited. In that design you can only specify multi-cycle paths between clock domains but it doesn't look like you use get_* at all. > set_multicycle_path -from Afe_AdcClk_ext -to Afe_ClockX4 -setup -end 2 > > Maybe give get_keepers a shot just to see if that is the syntax you need to use to get rid of the error "...was not applied to the design because none of the '-to' objects specified by the constraint exist in the design". Whether or not using multi cycle is useful in this situation (which as you found it was not), it is still useful to be able to use multicycle when it is appropriate (like a single signal crossing clock domains). > > Kevin Jennings
On 2023-07-15 KJ wrote in comp.arch.fpga:
...
> @Stef > Here are a couple of multicycle path statements from a working Quartus FPGA design. The only notable difference I see from what you posted is the use of get_keepers instead of get_cells as you had. If I recall correctly, Quartus uses Synopsys (or at least the same syntax). > > set_multicycle_path -setup -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}] 2 > set_multicycle_path -hold -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}] 2 >
... Thanks for those suggestions. I'll give them a try when I get back in a few weeks. -- Stef "Success covers a multitude of blunders." -- George Bernard Shaw