FONT SIZE : AAA
Most SoC designs include SoC technology elements that are not available in FPGA technology such as PLL, analog circuitry, BIST, SoC primitives and third-party IP. The following paragraphs describe some options to deal with SoC design elements that do not map into FPGA.
In the cases where SoC design elements are not available in FPGA technology, or where there is no desire to prototype certain blocks, these blocks need to be removed from the design. The removal may be as simple as removing a complete RTL design file from the project, leaving an unpaired module definition which might be inferred automatically as a black box. Alternatively, the RTL design file may need to be replaced with a dummy file which explicitly includes the necessary directive to set the module as a black box for synthesis.
In less tidy arrangements, the element to be removed may be buried in an RTL file alongside other logic that we wish to keep. In that case it may be necessary to alter the RTL, but a better approach, as we shall see in chapter 9, would be to predict at the time that the RTL was written that the element might need to be removed.
Figure 76: removing a block from the design using `ifdef
/* defining the compiler macro for prototyping only*/
`define FPGA;
<other rtl code>
/* soc_block is not instantiated when FPGA macro is defined
(leaving the commented instantiation line helps others to
understand what has been done. Proper documentation would also be
helpful ) */
`ifdef FPGA
/ soc_block block1 (signals.);
/* instantiating soc_block only when FPGA macro is not defined*/
`else
soc_block block1 (signals.);
`endif
<other rtl code>
Taking this approach, a conditional branch may have been placed into the original RTL code, based on a single macro. The example in Figure 76 shows the condition removal of a module using `define and `ifdef.
The rest of the design that would normally connect with the removed logic may be handled in different ways by different synthesis tools. Some tools will simply flag the condition as an error error-out because there are dangling connections, but other tools will also remove downstream logic which is not driven as a result of the change; either up to a block boundary or other synthesis-invariant point, up to and including the entire cone of downstream logic. Upstream, that logic which previously drove the removed block will also be pruned as far as any upstream sources which also drive other, non-pruned, cones of logic.
To illustrate this effect, let us consider the small excerpt from a design shown in Figure 77.
Let us suppose that Block A is not required in the prototype so we intend to simply remove it and allow the synthesis to prune out the unnecessary logic. The ripple-out effect of Block A’s removal upstream and downstream will depend upon the synthesis tool’s defaults and configuration. In Figure 78 we see that for a specific synthesis tool set-up, the upstream and downstream logic is mostly pruned as we might expect. Block A is removed along with all mutually exclusive upstream logic.
The AND gate is the point at which pruning stops because it is also driving other logic.
Downstream, two effects are instructive. Firstly, the downstream blocks receiving inputs from the removed block will be removed in turn unless they also receive data inputs from other parts of the design (clock or reset inputs are not sufficient). So in our example, Block B is completely removed, probably causing further pruning of ITS downstream logic. Block C will undergo pruning because some internal logic will not be driven when Block A is removed, while some other logic will remain because it is driven from other sources.
The second item of note is that the black box may not be removed by default and neither will its downstream logic, with the consequence that back-end tools will need to handle the lack of inputs, which may or may not be possible. This can manifest itself in some very subtle ways, for example, an instantiated BlockRAM may survive until place and route and then prevent completion because of nondriven inputs. Even worse may be the setting of non-driven inputs to unexpected values and the BlockRAM remains in circuit.
Thus, we should take care with simple removal of blocks as a method for reducing the size of our prototype. Trimming of upstream logic which is unique to the removed block is not usually a problem; however, pruning of downstream logic may have widespread and unpredictable effects.
Synthesis tool settings for cross-boundary optimizations will have an effect to promote or prevent the ripple-out of logic removal. There may also be tool-specific directives and attributes within the design or project files or the tool itself which control logic pruning. Readers are encouraged to explore their own FPGA synthesis tools in order to understand their behavior in these circumstances. For the Synopsys® example, Synplify Pro® synthesis tools have a directive called syn_noprune which, as the name suggests, prevents pruning of non-terminated or non-driven signals.
However, even if such a directive is used in synthesis, it may be that the place & route tools will have their own default operation which overrides the setting when dangling signals are found in the input netlist. It is good practice to ensure that every system or toolset used for the project is explicitly given a predictable setup rather than rely on defaults. If the members of our SoC and/or prototyping project teams are running different tools installations and seeing different results in their respective labs, then tool defaults and settings are a good place to start looking for the reasons.
So, block removal is a quick and powerful way to remove unwanted logic but may have unforeseen results. A better approach may be to replace the block with a simple set of constant values called stubs, which leave no room for ambiguity of tool dependence. There is more detail on the use of stubs in the next section.
To remove possible ambiguity between tool-flows as a result of element removal, as discussed above, a simple step is to add a dummy design file which explicitly ties off unused ports to desired values. Amending the example in section 7.5.1 aboveabove, we arrive at the following code:
We see that the code is altered to call a different version of the SoC block that included the stubs which tie off outputs to specific values, controlling downstream pruning. Inputs to the stub block can be ignored with the upstream pruning taking place as normal, alternatively, dummy registers or other logic might be used to sink the input signals or bring to an external test point using global signal (see next section). Another advantage of using stubs is that it more fully defines the design for simulation purposes. Simulators have a much more particular requirement for complete RTL code and will often error-out on signals etc. by default.
Figure 79: Substituting stub block using `ifdef
/* defining the compiler macro */
`define FPGA;
/ define stub block (preferably in separate file used only for
prototype)
module SoC_block1_stub (signals… )
input …;
output …;
/ elsewhere in the SoC design
<other rtl code>
/* replacing the SoC_block instance with stub equivalent */
`ifdef FPGA
SoC_block SoC_block1_stub (signals.);
/* instantiating SoC module SoC_block when FPGA macro is not
defined*/
`else
SoC_block SoC_block1 (signals.);
`endif
<other rtl code>
Therefore, stubs are a useful way to ensure repeatable pruning results, regardless of which tool we use or its setup.
In previous examples,`define and `ifdef are used to control the synthesis branching between the FPGA and SoC code at compile time. In some cases, original RTL designers prefer to keep their code free of implementation-specific alterations. This fits within their team’s style guidance of separation and modularity. It is seen to be “cleaner” to keep as much target-specific code out of as much of the RTL as possible. In general, this also leads to a more adaptable design. There are a couple additional options that avoid unwanted ifdef’s but still improve the design’s adaptability.
As suggested in a few of the examples, one option is to create “libraries” for each target, for example, by isolating a sub-list of the source files that is target-specific and only including the relevant sub-list for a given target. The good points of this approach are that it allows us to quickly compare the two lists and discern where there are differences between the two databases. The strong disadvantage is that there are still two databases, and notwithstanding the naturally higher level of separation as compared to some of the other common approaches, maintaining multiple databases means more work and frequently leads to negligence of the secondary database.
This disadvantage is minimized if the target-specific code is kept as isolated as possible from the remaining code. For example, if several SoC library primitives are instantiated directly in a large module, two copies of the large module would have to be maintained. On the other hand, if the instantiation is of the library component (containing in one case the SoC primitive and in another case the synthesizable, behavioral equivalent), there is more chance that the code can change locally, without having to implement the change in multiple files.
Figure 80: Use of VHDL Global Signal to extract signal to a test point
# Globals Package
package globals_pkg is
signal observe_1 : std_logic;
end globals_pkg;
# Low Level Arch
use WORK.globals_pkg.all;
architecture bhv of deep_down is
signal what_to_watch : std_logic;
. . .
Begin
observe_1 <= what_to_watch;
End Bhv;
# Top Level Arch
use WORK.globals_pkg.all;
architecture Bhv of top_design is
. . .
Begin
trace_pin <= observe_1;
End;
Some users find that the use of XMRs (cross-module references) in Verilog or Global signals, the VHDL equivalent, helps to decrease the scope of RTL changes. In Figure 80 we see an example of bringing an internal node out to a test pin using a VHDL Global signal. In this way we need change only three files and the only boundary change is at the top-level block in order to add the test pin itself. Another good use of XMRs is to inject signals into a lower level. For example, one of the common needs in making a design FPGA-ready is to simplify clocking. To make this easier, SoC RTL writers are asked to keep clock generators, gates etc. in a common block at the top level. Their reason for not doing so is often that it would complicate the hierarchy boundaries to push the clocks down to the low-level modules where the clocks are used. XMRs can overcome that objection. XMRs may also be used retrospectively by the prototyping team to achieve much the same goal.
Another approach is to use a netlist editing utility which may accompany some synthesis or partitioning tools. We explicitly specify the differences between the original database and any modifications required for the prototype directly in the synthesis netlist. The netlist editor would be run after each synthesis run, usually from a common script. This approach can be thought of as a series of overlays on the original RTL with the target-specific library compiled in parallel with the design and then stitched into the design using the netlist editor. This has all the advantages of the multiple filelist approach, maintains and utilizes the original golden SoC RTL without changes, and eliminates most issues associated with keeping RTL databases in sync.
Other approaches include using code generators and project generators. These are frequently written in Perl, C, etc., or using makefiles, or combinations thereof. In practice, most projects use combinations of approaches. For example, a project could use ifdef’s for the library selection, or include them in the library itself.
However it is done, the target-specific code should be architected so that it is tightly bound to local sections of the design. Keeping the effect close to the cause avoids confusion. The best approach is the one that minimizes the impact on the design and maximizes the ease and simplicity of switching between targets.
In the cases where a non-synthesizable SoC design element is needed in the FPGAbased prototyping effort, it may be replaced with synthesizable RTL code. In Figure 81 we see an example of an SoC primitive SoC_mux being replaced with RTL code:
It is obviously important to verify that the RTL is functionally equivalent to the SoC module it’s replacing. Therefore some simulation of the replacement alongside the original, perhaps with assertions checking for differences, would be very useful. We discuss such an approach with respect to memories in section 7.7.3 below.
Figure 81: Replacing instantiation with behavioral code
/* defining the compiler macro */
`define FPGA true;
module SoC_top (…inputs, outputs…);
input ……;
output ……;
/some internal signals definitions
wire sel;
wire in1;
wire in2;
wire mux_out;
/* replacing the SoC_mux instance with RTL */
`ifdef FPGA
assign mux_out = sel ? in1 : in2;
/* instantiating SoC module SoC_mux when FPGA macro is not
defined*/
`else
SoC_mux SoC_mux1 (
.input1(in1),
.input1(in1),
.select(sel),
.output(mux_out)
);
‘endif
endmodule
Recommendation: It’s important to note that if the remaining design is not properly terminated after removal of some blocks then the synthesis tool may optimize out (i.e., remove) any logic that is not driven by, or is not driving, other logic as a result. Therefore additional design modifications or synthesis directives may be necessary, such as creating stub designs.
In many cases the SoC element which requires replacement is an instantiated leaf cell from the technology library. A good source of equivalent functionality for such an instantiated SoC element can be found in the actual technology library used for the SoC.
The liberty (.lib) format for technology libraries includes an equation for the functionality for each leaf cell. We can see such a function for a basic cell in the small excerpt from a .lib file shown in Figure 82 and we can see the function of the output pin Y in logical terms of the inputs. It is a simple matter to convert this “function” equation into equivalent RTL, although there are EDA utilities which can perform the same task quickly and without error.
As an example, FPGA synthesis tools from Synopsys have the ability to read the .libfile directly and refer to the cell description in order to resolve instantiations of leaf cells in the RTL.
Figure 82: Excerpt from an AND-OR cell description in .lib file
cell (AOI22XL) {
cell_footprint : aoi22;
area : 10.1844;
pin(A0) {
direction : input;
}
pin(A1) {
direction : input;
}
pin(B0) {
direction : input;
}
pin(B1) {
direction : input;
}
pin(Y) {
direction : output;
function : "(!((A0 A1) | (B0 B1)))";
Figure 83 shows part of an RTL view of a gate-level netlist after being compiled into FPGA synthesis. Note that the blocks are leaf-cell elements from the technology library used for the SoC synthesis.
As noted previously, each of these cells would normally be interpreted by FPGA synthesis as a black box. Luckily, the source .lib file for the technology is available to the prototyping team and we can add this to our synthesis project as any other design file. The tools then automatically extract the functionality as mentioned, mapping it to FPGA resources during synthesis as normal.
If the .lib file is not available, or we are not using a synthesis tool that supports this flow, then a colleague supporting the SoC tool flow might be able to create a Verilog file from the .lib to add into the FPGA project. That Verilog file would only be a set of module definitions, which could be created using a utility such as Synopsys lib2syn, or even by a script which extracts the function from the .lib file and transcribes it into a Verilog module declaration.
In some cases SoC elements can be substituted by equivalent FPGA design elements or “cores.” These FPGA cores are special-purpose FPGA entities used to optimize FPGA implementation for area and or performance. Common examples of FPGA primitives are memory blocks or shift register functions.
Usually, we do not need to simply replace an SoC element with an FPGA equivalent but instead we replace it with an RTL description and allow the synthesis tools to map it into FPGA elements by inference. The example in
Figure 84 shows an 8x4 synchronous RAM module soc_ram being replaced by RTL code. In this case, the FPGA memory block will be inferred by the synthesis tool during synthesis.
The RTL enabled by the FPGA macro will be interpreted by the FPGA synthesis as RAM and mapped into the relevant resource in the FPGA(s).
Figure 84: Instantiated RAM replaced by inferred equivalent
/* defining the compiler macro */
`define FPGA true;
module soc_top (…inputs, outputs…);
input ……;
output ……;
/some internal signals definitions
wire [3:0] mem_out;
wire [3:0] mem_in;
wire [2:0] addr;
wire we;
wire clk;
/* replace soc_ram instance with RTL when compiling for FPGA */
`ifdef FPGA
reg [3:0] mem [7:0];
assign mem_out = mem[addr];
always @(posedge clk)
if(we) mem[addr]= mem_in;
/* instantiating SoC module soc_ram when compiling for SoC */
`else
soc_ram soc_ram1 (
.mem_do(mem_out),
.mem_di(mem_in),
.mem_addr(addr),
.mem_clk(clk),
.mem_we(we)
);
`endif
endmodule
In addition to memory blocks, FPGAs have some special-purpose blocks that may be needed for the prototyping effort. Examples of such blocks are high-speed serial interface blocks (also known as SERDES), DDR memory interfaces and FIFOs. Since these special-purpose blocks are highly programmable, they are not always inferred by the synthesis tools and instead they must be instantiated directly into the design.
The process of including such elements is as follows:
• Create an FPGA element using the FPGA vendor’s supplied tools (such as the Xilinx® CORE Generator™ tool, Memory Interface Generator etc.) Typically the user selects the core type, the target technology and defines the various parameters’ initial state values etc.
• The FPGA tool generates the desired core’s FPGA netlist and initialization file –where applicable – that are used in the place & route stage, and a template file used to instantiate the generated core into the main design.
• In addition, the tool generates a wrapper file containing functional simulation customization data that, combined with the primitive model used in the core, can be used for functional simulation.
• We then instantiate the template file in the design and connects the module to the design.
Figure 85: Instantiation template created by Xilinx® CORE Generator tool
/----------- Begin Cut here for INSTANTIATION Template ---/
/ INST_TAG
fifo_generator_v5_1 YourInstanceName (
.clk(clk),
.din(din), / Bus [17 : 0]
.rd_en(rd_en),
.rst(rst),
.wr_en(wr_en),
.dout(dout), / Bus [17 : 0]
.empty(empty),
.full(full));
/ INST_TAG_END ------ End INSTANTIATION Template ---------
Copyright © 2011 Xilinx, Inc.
Figure 85 shows an instantiation of a FIFO template generated by the Xilinx® CORE Generator tool:
Figure 86: use of FIFO module in defined using the template in Figure 85
/* instantiating the above FIFO module in the design “top”;
module top (input… outputs…);
/* defining the compiler macro */
`define FPGA true;
module top (input… outputs…);
.
<other RTL code>
.
`ifdef FPGA
/* instantiation of FPGA FIFO */
fifo_generator_v5_1 MyFIFO(
.clk(Myclk),
.din(Mydin),
.rd_en(Myrd_en),
.rst(Myrst),
.wr_en(Mywr_en),
.dout(Mydout),
.empty(Myempty),
.full(Myfull));
`else
/original SoC primitive instance
soc_FIFO MyFIFO (
.clk(Myclk),
.soc_din(Mydin),
. soc_rd_en(Myrd_en),
. soc_rst(Myrst),
. soc_wr_en(Mywr_en),
. soc_dout(Mydout),
. soc_empty(Myempty),
. soc_full(Myfull));
‘endif
endmodule
The FIFO template would be instantiated in place of SoC FIFO module as shown in Figure 86, once again using the FPGA macro to branch between implementations.
In the example, the synthesis tool will generate a netlist with an FPGA equivalent black box in place of the SoC black box. The contents are added in when the design reaches the back-end and the association is made by the model name in the template.
When instantiating a core for which an FPGA netlist exists, the synthesis tool usually applies timing constraints to the core. Furthermore, depending on the synthesis tool, it may also be possible to include the FPGA netlist during synthesis so that further optimization may occur. In this case, we refer to the module as a gray box.
There may be situations where within the same design the use of special-purpose FPGA resources is desired for some instances, but not desired for other instances. Such situations can happen due to the finite number of available resources or the locations of these resources and the way they are connected into the design. For example, in some Xilinx® FPGA families, dedicated 48-bit multiplier blocks are available in fixed columns on the die. By default, the FPGA synthesis should map to dedicated resources but we also need to be able to override the default decisions in some situations. For example, when routing delays to a multiplier from the rest of the design placement would outweigh the performance gain in using it. In that case it would be better instead to implement the multiply function in general-purpose distributed logic. So there is a need to selectively direct the synthesis tool to infer the dedicated multipliers for some multiply functions and to
Figure 87: RTL excerpt showing control of DSP48 mapping using synthesis directive
<other RTL statements>
. . .
Reg [7:0] x1_in1, x1_in2; /inputs to multiplier 1
Reg [7:0] x2_in1, x2_in2; /inputs to multiplier 2
wire [15:0] mult_out1 /* synthesis syn_dspstyle = “dsp48” */;
wire [15:0] mult_out2 /* synthesis syn_dspstyle = “logic” */;
/* a DSP48 will be inferred for the multiplier driving mult_out1
*/
/* a DSP48 will not be inferred for the multiplier driving
mult_out2 */
Assign mult_out1 <=x1_in1 * x1_in2;
Assign mult_out2 <=x2_in1 * x2_in2;
In that case it would be better instead to implement the multiply function in general-purpose distributed logic. So there is a need to selectively direct the synthesis tool to infer the dedicated multipliers for some multiply functions and to map to logic in other cases. A synthesis attribute should be available to control these kinds of decisions and override the default.
In the RTL in Figure 87 we see the use in Synopsys FPGA synthesis of an attribute called syn_dspstyle. This attribute can take one of two values: “logic” or “dsp48” and it is used to direct the synthesis to infer or not to infer the 48-bit fixed multiplier.
Note that this attribute also applies to other entities that can be mapped into the DSP48 block such as adders and registers.
In some cases, this process can be done automatically when synthesis infers the use of DSP and RAM blocks. The inference is timing-driven and often paths are retimed to get better DSP and RAM packing, but also a running count of DSP and RAM usage is maintained so that if the resource limit for the target FPGA is overflowed. Then some of the design that might otherwise infer DSP and RAM blocks will be automatically mapped into other logic resources instead.
Manufacturer:Xilinx
Product Categories: CPLDs
Lifecycle:Active Active
RoHS:
Manufacturer:Xilinx
Product Categories: CPLDs
Lifecycle:Active Active
RoHS:
Manufacturer:Xilinx
Product Categories: CPLDs
Lifecycle:Active Active
RoHS: No RoHS
Manufacturer:Xilinx
Product Categories: CPLDs
Lifecycle:Active Active
RoHS:
Manufacturer:Xilinx
Product Categories: Embedded - CPLDs (Complex Programmable Logic Devices)
Lifecycle:Active Active
RoHS: No RoHS
Support