Did the code you wrote behave normally in the emulator, but it was intermittently in the field? Otherwise it is possible that it starts to go wrong when you compile with a later version of the toolchain. You check your test platform and verify that the test has achieved 100% full coverage, and that all tests have not experienced any errors, but the problem is still difficult to remove.
Although designers place great emphasis on coding and simulation, they know little about the internal operation of the chip in FGPA, which is understandable. Therefore, incorrect logic synthesis and timing issues (rather than logic errors) are the source of most logic failures.
However, as long as the designer is well-equipped, it is easy to write FPGA code that creates predictable, reliable logic.
In the FPGA design process, logic synthesis and related timing closure are required during the compilation phase. And many aspects, including I / O unit structure, asynchronous logic and timing constraints, will have a huge impact on the compilation process, so that each round will produce different results in the tool chain. To complete timing closure better and faster, let's explore how to eliminate these differences.
I/O Cell Structure All FPGAs have highly customizable I/O pins. Customization can affect timing, drive strength, termination, and many other aspects. If you don't explicitly define the I/O unit structure, your toolchain will often adopt the default structure that you expect or don't want to adopt. The purpose of the following VHDL code is to create a bidirectional I/O buffer called sda with the "sda: inout std_logic;" declaration.
Tri_state_proc : PROCESS (sys_clk)BEGINif rising_edge(sys_clk) thenif (enable_in = '1') thensda <= data_in;elsedata_out <= sda;sda <= 'Z';end if;end if;END PROCESS tri_state_proc;
When the synthesis tool discovers this set of code, it lacks a clear indication of how to implement a bidirectional buffer. Therefore, the tool will make the most reasonable guess.
One way to accomplish this is to use a bidirectional buffer on the I/O ring of the FPGA (in fact, this is an ideal implementation). Another option is to use a three-state output buffer and an input buffer, both of which are implemented in look-up table (LUT) logic. The last possible method is to use a three-state output buffer on the I/O loop and an input buffer in the LUT, which is the method chosen by most synthesizers.
All three methods can generate valid logic, but the latter two embodiments generate longer routing delays when transmitting signals between I/O pins and LUTs. In addition, they require additional timing constraints to ensure timing closure. The FPGA editor clearly shows that in Figure 1, some of our bidirectional I/O is spread out of the I/O buffer.
The lesson is to remember not to let the synthesis tool guess how to implement the key parts of the code. Even if the integrated logic happens to meet your expectations, the situation may change as the composite tool enters the new version.
Your I/O logic and all key logic should be clearly defined. The following VHDL code shows how to implicitly define an I/O buffer using the Xilinx® IOBUF primitive. It is also important to note that all electrical characteristics of the buffer are clearly defined in a similar manner.
sda_buff: IOBUFg enericmap (IOS TANDARD => "LVCMOS25", IFD_DELAY_VALUE => "0", DRIVE => 12, SLEW => "SLOW") port map (o => data_out, io => sda, i => data_in, t=> enable_in);
In Figure 2, the FPGA editor clearly shows that we have implemented bidirectional I/O entirely inside the I/O buffer.
The inferior asynchronous code of asynchronous logic creates logic that is difficult to constrain, emulate, and debug. Asynchronous logic tends to produce intermittent errors that are almost impossible to reproduce. In addition, it is not possible to generate a test platform for detecting errors caused by asynchronous logic.
While asynchronous logic may seem easy to detect, it is in fact often undetected; therefore, designers must be careful about many aspects of asynchronous logic that are hidden in the design. All clocking logic requires a minimum setup and hold time, and this also applies to the reset input of the trigger. The following code uses an asynchronous reset. Timing constraints cannot be applied here to meet trigger setup and hold time requirements.
data_proc: PROCESS (sys_clk, reset) BEGINif (reset = '1') thendata_in <= '0'; elsif rising_edge (sys_clk) thendata_in <= serial_in; end if; END PROCESS data_proc;
The following code uses a synchronous reset. However, the reset signal for most systems may be a push button switch or other source independent of the system clock. Although the reset signal is mostly static and is asserted or de-asserted for a long time, its level will still change. Equivalent to the rising edge of the system clock, the reset deassertion can violate the setup time requirement of the trigger, which cannot be constrained.
data_proc: PROCESS (sys_clk) BEGINif rising_edge (sys_clk) thenif (reset = '1') thendata_in <= '0'; elsedata_in <= serial_in; end if; end if; END PROCESS data_proc;
As long as we understand that it is not possible to feed asynchronous signals directly into our synchronization logic, it is easy to solve this problem. The following code creates a new reset signal called sys_reset that has been synchronized with our system clock, sys_clk. A metastability problem arises when sampling asynchronous logic. We can use the trapezoidal sampling of the 'and' operation with the first few steps of the ladder to reduce the probability of this problem.
Data_proc : PROCESS (sys_clk)BEGINif rising_edge(sys_clk) thenreset_1 <= reset;reset_2 <= reset_1 and reset;sys_reset <= reset_2 and reset_1and reset;end if;if rising_edge(sys_clk) thenif (sys_reset = '1') thendata_in <= '0';elsedata_in <= serial_in;end if;end if;END PROCESS data_proc;
At this point, assume that you have carefully implemented synchronization of all logic. However, if you are not careful, your logic is easily out of touch with the system clock. Do not let your toolchain use the local routing resources used by the system clock. If you do that, you can't constrain your logic. Remember to clearly define all the important logic.
The following VHDL code uses the Xilinx BUFG primitive to force sys_clk into a dedicated high fanout buffer that drives low-skew nets.
Gclk1: BUFG port map (I => sys_clk, O=> sys_clk_bufg); data_proc : PROCESS (sys_clk_bufg)BEGINif rising_edge(sys_clk_bufg) thenreset_1 <= reset;reset_2 <= reset_1 and reset;sys_reset <= reset_2 and reset_1and reset;end if ; if rising_edge (sys_clk_bufg) thenif (sys_reset = '1') thendata_in <= '0'; elsedata_in <= serial_in; end if; end if; END PROCESS data_proc;
Some designs use a split version of a single master clock to process deserialized data. The following VHDL code (nibble_proc process) illustrates data collected at a quarter of the system clock frequency.
Data_proc : PROCESS (sys_clk_bufg)BEGINif rising_edge(sys_clk_bufg) thenreset_1 <= reset;reset_2 <= reset_1 and reset;sys_reset <= reset_2 and reset_1and reset;end if;if rising_edge(sys_clk_bufg) thenif (sys_reset = '1') thentwo_bit_counter <= "00"; divide_by_4 <= '0'; nibble_wide_data <= "0000"; elsetwo_bit_counter <= two_bit_counter + 1; divide_by_4 <= two_bit_counter (0) andtwo_bit_counter (1); nibble_wide_data (0) <= serial_in; nibble_wide_data (1) < = nibble_wide_data (0); nibble_wide_data (2) <= nibble_wide_data (1); nibble_wide_data (3) <= nibble_wide_data (2); end if; end if; END PROCESS data_proc; nibble_proc: PROCESS (divide_by_4) BEGINif rising_edge (divide_by_4) thenif (sys_reset = '1') thennibble_data_in <= "0000";elsenibble_data_in<= nibble_wide_data;end if;end if;END PROCESS nibble_proc;
It seems that everything has been synchronized, but nibble_proc uses the product term divide_by_4 to sample nibble_wide_data from the clock domain sys_clk_bufg. Due to routing delay, there is no clear phase relationship between divde_by_4 and sys_clk_bufg. Transferring divide_by_4 to BUFG also does not help, because this process will generate routing delays. The solution is to keep nibble_proc in the sys_clk_bufg field and use divide_by_4 as a qualifier, as shown below.
Nibble_proc : PROCESS (sys_clk_bufg)BEGINif rising_edge(sys_clk_bufg) thenif (sys_reset = '1') thennibble_data_in <= "0000";elsif (divide_by_4 = '1') thennibble_data_in<= nibble_wide_data;end if;end if;END PROCESS nibble_proc
The Importance of Timing Constraints If you want your logic to work correctly, you must use the correct timing constraints. These steps can significantly simplify timing closure if you have carefully ensured that the code is fully synchronized and all I/O is registered. With the above code and assuming that the system clock is 100MHz, the timing constraint file can be easily completed with just four lines of code, as shown below:
NET sys_clk_bufg TNM_NET = sys_clk_bufg; TIMESPEC TS_sys_clk_bufg = PERIODsys_clk_bufg 10 ns HIGH 50%; OFFSET = IN 6 ns BEFORE sys_clk; OFFSET = OUT 6 ns AFTER sys_clk;
Please note that the setup and hold times of I/O registration logic in Xilinx FPGAs are highly fixed and should not be changed much in a package. However, we still use them primarily as a verification step to ensure that the design meets its system parameters.
The three-step simple operation requires only three simple steps to follow, making it easy for designers to implement reliable code.
• Do not let the synthesis tool guess what you expect. All I/O pins and critical logic are clearly defined using Xilinx primitives. Ensure that the electrical characteristics of the I/O pins are defined; • Ensure that the logic is 100% synchronized and that all logic references the main clock domain; • Apply timing constraints to ensure timing closure.
By following the three steps above, you can eliminate the differences caused by synthesis and timing. Removing these two major barriers will give you code with 100% reliability.
High Current Terminal Blocks,Panel Terminal Block,Feed Through Terminal Block,Heavy Power Terminal Block
Sichuan Xinlian electronic science and technology Company , https://www.sztmlchs.com