FPGA timing constraints - Page 1
Forum section: electronics / general

author: gauravmp
author_rank: Regular Contributor
timestamp: September 06, 2016, 12:55:31 am
content: Hello,I'm trying to wrap my head around setting timing constraints and I'm having a hard time. My implementation fails timing. I know this also has to do with my code probably but could someone give me pointers as to how you would tackle a failed timing scenario?I also have questions regarding how to ascertain a setup or hold time for input and output ports.Any help would be appreciated.Thanks

author: jonrc
author_rank: Newbie
timestamp: September 06, 2016, 11:38:27 am
content: Hi, timing is a ... If you are using TimeQuest (Altera) Then there is a guy name Ryan Scoville, who have made some papers on Timequest, which is worth a look.--

author: AndyC_772
author_rank: Super Contributor
timestamp: September 06, 2016, 11:51:12 am
content: Ah, my 'favourite' subject. Can open, worms everywhere.There are basically two reasons why your design might fail timing. Either:a) You're trying to do too much per clock cycle, ie. the real, inherent delays in the FPGA fabric conspire to mean your logic cannot run that fast. You need a faster grade of FPGA, or perhaps more likely, a review of your code to work out where the bottlenecks are, and a way to speed them up. Or:b) You have more than one independent timing source in your design, and some signal or signals which depend on both, such that it's simply impossible to ever be sure that a signal derived in one clock domain will be stable when read from another. A very common case is when a configuration register is set by a CPU, but its effect controls some logic that runs off a different clock.In case (b), you need to identify what all the clock sources are in your design, what relationship exists (if any) between them, and how you'll manage communication between one clock domain and another. Typically this involves double-sampling at a minimum, or a dual-clock FIFO for larger amounts of data.Make no mistake, this is a whole specialist area of engineering in its own right. IC manufacturers have whole teams of engineers whose job it is to specify, derive and check timing constraints. Personally I'd rather jack in doing electronics and farm potatoes for a living instead, but as an FPGA designer it's impossible to get away from this topic completely.

author: gauravmp
author_rank: Regular Contributor
timestamp: September 06, 2016, 01:21:11 pm
content: Thanks for your reply. Specifically, I noticed that I have issues in timing when I get a pll in. I'm using a Xilinx Artix-7 device and Vivado 2016.1. Also for the pll, I just used the clocking wizard.If you think you can help me with my code, here it is:module fpga_top(clk,cs,spi_sdo,spi_sck,tx);//Port Declarations:input clk;output cs;output spi_sdo;output spi_sck;output tx;wire clk;wire cs;wire spi_sdo;wire spi_sck;wire tx;//General decreg rst;reg[27:0] count;reg[9:0] rst_count;//SPI declarationsreg[7:0] spi_data;reg spi_en;//UART declarationswire[7:0] tx_data;wire tx_done;wire clk_baud8;reg tx_en;initialbeginspi_data <= 65;count <= 0;rst <= 1;spi_en <= 0;tx_en <= 0;rst_count <= 500;end//PLL instantiationclk_wiz_0 inst(.clk_in1(clk),.clk_out1(clk_baud8),.reset(rst),.locked(locked));//SPI instantiation:spi inst_spi(.clk(clk),.rst(rst),.spi_en(spi_en),.data(spi_data),.cs(cs),.sdo(spi_sdo),.sck(spi_sck),.spi_done(spi_done));//UART instantiationuart inst_uart(.clk(clk),.clk_baud8(clk_baud8),.rst(rst),.tx(tx),.tx_done(tx_done),.tx_data(tx_data),.tx_en(tx_en));//initial resetalways @(posedge clk)beginif(rst_count > 0)rst_count <= rst_count - 1;elserst <= 0;endalways @(posedge clk)beginif(~rst)begincount <= count + 1;if(count == 100000000)count <= 0;endend//SPI controlleralways @(posedge clk)beginif(count == 50000000)spi_en = 1;if(spi_done)spi_en = 0;if(count == 75000000)spi_data <= spi_data + 1;if(spi_data == 127)spi_data <= 1;end//UART controllerassign tx_data = spi_data;always @(posedge clk)beginif(count == 50000000)tx_en = 1;if(tx_done)tx_en = 0;endendmodulemodule uart(clk,clk_baud8, //clk_baud8 is oversampled by 8 timesrst,tx,tx_data,tx_done,tx_en//debugging purposes// ,// state,// shift_count,// falling,// baud_counter,// baud_tick);input clk;input rst;input clk_baud8;input[7:0] tx_data;input tx_en;output tx;output tx_done;//debugging purposes//output[1:0] state;//output[3:0] shift_count;//output falling;//output[2:0] baud_counter;//output baud_tick;wire clk;wire rst;wire clk_baud8;wire[7:0] tx_data;wire tx_en;wire tx;wire tx_done;wire baud_tick;reg temp_tx;reg[1:0] state;reg[3:0] baud_counter;reg[9:0] shift_register;reg falling;reg[3:0] shift_count;parameter[1:0] idle = 2'b00;parameter[1:0] sending = 2'b01;parameter[1:0] done = 2'b10;initialbegintemp_tx <= 1;state <= idle;baud_counter <= 0;shift_register <= 0;falling <= 0;shift_count <= 0;endassign tx = temp_tx;assign baud_tick = ~baud_counter; //baud rate generatorassign tx_done = (state == done)? 1'b1:1'b0;//divided clk_baud8 for 115200bpsalways @(posedge clk_baud8)beginif(state == sending)baud_counter <= baud_counter + 1;elsebaud_counter <= 0;end//state machinealways @(posedge clk)beginif(rst)state <= idle;elsebegincase(state)idle:beginif(tx_en)state <= sending;endsending:beginif((shift_count == 10)&(falling == 0))state <= done;enddone:beginif(tx_en == 0)state <= idle;enddefault: state <= idle;endcaseendend//implementationalways @(posedge clk)beginif(state == idle)beginshift_count <= 0;temp_tx <= 1;shift_register <= tx_data;shift_register <= {1'b0,tx_data[7:0],1'b1}; //appending start and stop bits// shift_register <= {shift_register[8:0],1'b1}; //appending stop bitendelse if(state == sending)beginif((baud_tick == 0)&(falling == 0))beginfalling <= 1;temp_tx <= shift_register;shift_register <= {shift_register[8:0],1'b1};shift_count <= shift_count + 1;endelse if(baud_tick == 1)falling <= 0;endendendmodulemodule spi(clk,rst,spi_en,data,cs,sdo,sck,spi_done);//Port Declarations:input clk;input rst;input spi_en;input[7:0] data;output cs;output sdo;output sck;output spi_done;wire clk;wire rst;wire spi_en;wire[7:0] data;wire cs;wire sdo;wire sck;wire spi_done;wire clk_divided; //6.25 MHzreg[1:0] state;reg[7:0] shift_reg;reg[3:0] shift_count;reg[4:0] counter;reg[3:0] cs_counter;reg falling;reg temp_sdo;reg temp_cs;reg cs_done;parameter[1:0] idle = 2'b00;parameter[1:0] chip_select = 2'b01;parameter[1:0] sending = 2'b10;parameter[1:0] done = 2'b11;parameter[2:0] cs_cnt_num = 3'd5; //50nsinitialbeginstate <= idle;shift_reg <= 0;shift_count <= 0;temp_cs <= 0;counter <= 0;falling <= 0;temp_sdo <= 1;cs_counter <= 0;cs_done <= 0;endassign clk_divided = ~counter;assign sck = clk_divided;assign sdo = temp_sdo;assign cs = temp_cs;assign spi_done = (state == done) ? 1'b1 : 1'b0;//clock divideralways @(posedge clk)beginif(state == chip_select)cs_counter <= cs_counter + 1;else if(state == sending)counter <= counter + 1;elsebegincounter <= 0;cs_counter <= 0;endend//state machinealways @(posedge clk)beginif(rst)state <= idle;elsebegincase(state)idle:beginif(spi_en)state <= chip_select;endchip_select:beginif(cs_done)state <= sending;endsending:beginif((shift_count ==&&(falling == 0))state <= done;enddone:beginif(spi_en == 0)state <= idle;enddefault:state <= idle;endcaseendend//implementationalways @(posedge clk)beginif(state == idle)beginshift_count <= 0;shift_reg <= data;temp_sdo <= 1;cs_done <= 0;temp_cs <= 1;endelse if(state == chip_select)beginif(cs_counter == cs_cnt_num)begincs_done <= 1;endendelse if(state == sending)begintemp_cs <= 0;if(clk_divided == 0 && falling == 0)beginfalling <= 1;temp_sdo <= shift_reg;shift_reg <= {shift_reg[6:0],1'b0};shift_count <= shift_count + 1;endelse if(clk_divided == 1)falling <= 0;endendendmoduleThanks a lot!!!!!

author: AndyC_772
author_rank: Super Contributor
timestamp: September 06, 2016, 06:20:13 pm
content: Sorry, I really wasn't kidding (much) about the potatoes.I'm afraid that if the issues surrounding metastability, crossing clock domains and meeting setup & hold requirements are news to you, then you have a great deal of background reading to go and do before you'll really be able to understand why you're getting timing errors. I'm not trying to be mean; it's just that timing is such a fundamental part of FPGA design that every designer needs to be familiar with it.Although the basic issues are the same regardless, I use Altera parts and write in VHDL, so I couldn't reproduce or fix your code anyway.

author: gauravmp
author_rank: Regular Contributor
timestamp: September 06, 2016, 10:28:11 pm
content: Quote from: AndyC_772 on September 06, 2016, 06:20:13 pmOh no. You're not being mean. I know I have a lot of reading to do. The problem is, I'm having a hard time trying to figure out sources to read from. Could you pls suggest some readings? Thanks

author: Someone
author_rank: Super Contributor
timestamp: September 06, 2016, 10:40:27 pm
content: Quote from: gauravmp on September 06, 2016, 01:21:11 pmThat is only half the problem.For example:I really like chopping wood, why can't I chop enough wood? I chop the end of the wood but its too slow, I like my Binford 6100 axe but why can't I chop enough wood?Without specifying:How long you are chopping forQuote from: gauravmp on September 06, 2016, 01:21:11 pmThere is no detail on the constraints you are putting on the design, and you're using two clocks without mentioning their relationship to each other. The usual advice for people learning FPGAs is to work within a single clock domain until they are competent in doing timing closure, additional clocks and their configuration are very complicated topics and have no rote methods.Xilinx has an entire 200 page book on this topic specific to your tools, UG906 "Vivado Design Suite User Guide, Design Analysis and Closure Techniques" but its only an overview, timing closure is the majority of FPGA design work time, trying to fit more functions into fewer resources or achieving latency requirements.

author: filssavi
author_rank: Frequent Contributor
timestamp: September 06, 2016, 10:44:31 pm
content: If you are not familiar with it read carefully the part about clocks in the rabaey, digital integrated circuits it will get you up to Speed about clocks (skew, slack, setup and hold times etc) then since every FPGA toolchain has it's method of specifing time constraints/report timing errors I think the documentation/manual is the place i would look nextAs a final advice i can give you is that, after you have done your Reading and you understand why the toolchain is yelling at you (vivado for example will give you in a nice report all the details of the offending path with the relative violation setup or hold time), you can do a simpler design with only the offending module/code and make it work alone

author: rstofer
author_rank: Super Contributor
timestamp: September 06, 2016, 10:49:22 pm
content: Code:beginif(state == idle)beginshift_count <= 0;temp_tx <= 1;shift_register <= tx_data;shift_register <= {1'b0,tx_data[7:0],1'b1}; //appending start and stop bits// shift_register <= {shift_register[8:0],1'b1}; //appending stop bitendelse if(state == sending)beginif((baud_tick == 0)&(falling == 0))beginfalling <= 1;temp_tx <= shift_register;shift_register <= {shift_register[8:0],1'b1};shift_count <= shift_count + 1;endelse if(baud_tick == 1)falling <= 0;endendI don't know anything about Verilog but I have to wonder what happens if state == done.In VHDL, incomplete cases are an issue. Which goes back to the idea of default assignments or even default next states.In concept, you should be able to run at 50 MHz and maybe 100 MHz without timing problems. I'm a little concerned about that Baud8 counter. Rather than look for an edge, how about making the baud clock just one Clk cycle wide. Just use a divider from Clk and make a signal equal to maximum count.

author: rstofer
author_rank: Super Contributor
timestamp: September 07, 2016, 12:48:31 am
content: Code://clock divideralways @(posedge clk)beginif(state == chip_select)cs_counter <= cs_counter + 1;else if(state == sending)counter <= counter + 1;elsebegincounter <= 0;cs_counter <= 0;endendif, else if, else creates a priority tree of logic. Maybe with two expressions to evalute it isn't so bad but maybe a case statement is better. You can write a couple of modules using priority tree and case statements then look at the RTL schematic.Make sure that all possible cases are covered - usually by adding a 'default' case.

author: gauravmp
author_rank: Regular Contributor
timestamp: September 07, 2016, 03:04:09 am
content: Quote from: filssavi on September 06, 2016, 10:44:31 pmThanks a lot for this! I will definitely read this book!

author: gauravmp
author_rank: Regular Contributor
timestamp: September 07, 2016, 03:07:12 am
content: Quote from: Someone on September 06, 2016, 10:40:27 pmMy apologies. I should have included a few comments. clkbaud8 is 115200 times 8. and clk is a 100MHz source. I will also look into the app guide you referenced

author: gauravmp
author_rank: Regular Contributor
timestamp: September 07, 2016, 03:59:10 am
content: Thank you everyone else as well for your replies. I will note your points

author: AndyC_772
author_rank: Super Contributor
timestamp: September 07, 2016, 06:37:07 am
content: One immediately obvious potential issue is that you have two clocks in your design, 'clk' and 'clk_baud8'.Within the clk_baud8 domain you're reading the value of 'state', which is set in the 'clk' domain.You're also writing a value to 'baud_counter', which controls 'baud_tick' (asynchronously). This is OK, but 'baud_tick' is read in the 'clk' domain which isn't.This means you have two instances in which signals are set in one clock domain but read in another.When you instantiate a PLL, there's an implicit timing relationship between the phases of the input clock and output clocks. Suppose for the sake of argument that output edges occur just fractionally after input edges.Now, further suppose that the value of 'state' is updated on the edge of the clock which is used as the PLL input. It can't then be read by logic clocked from the PLL output, because its value is still changing at the time the PLL output clock edge arrives.The amount of time between the two active edges is nothing to do with the clock speeds, it's to do with the phase relationship between them. This may be something you can define.Since your logic is fairly simple, and is running at serial port speeds which are really slow by today's standards, I wouldn't recommend using two separate clocks at all. Generate one clock which is the fastest you need, run all your logic from it, and use a counter to govern when activity needs to happen, ie:Code:counter <= counter + 1;if counter = 7 thencounter = 0;do interesting stuff;end if;

author: gauravmp
author_rank: Regular Contributor
timestamp: September 08, 2016, 03:39:39 am
content: Quote from: AndyC_772 on September 07, 2016, 06:37:07 amThanks a lot Andy!!!! I really appreciate you taking the time to read my code. Thanks a lot for those suggestions!! I can't thank you enough. I now understand what I should be looking for. I really was not very well versed in clock domains.