FONT SIZE : AAA
With regard to the traditional HDL-based flows introduced in Chapter 5, a design commences with an original concept, whose high-level definition is determined by system architects and system designers. It is at this stage that macro-architecture decisions are made, such as partitioning the design into hardware and software components.
The resulting specification is then handed over to the hardware design engineers, who commence their portion of the development process by performing microarchitecture definition tasks such as detailing control structures, bus structures, and primary data path elements. These microarchitecture definitions, which are often performed in brainstorming sessions on a whiteboard, may include performing certain operations in parallel versus sequential, pipelining portions of the design versus nonpipelining, sharing common resources (for example, two operations sharing a single multiplier, versus using dedicated resources) and so forth.
Eventually, the design intent is captured by writing RTL VHDL/Verilog. Following verification via simulation, this RTL is then synthesized down to a structural netlist suitable for use by the target technology’s place-and-route applications ( Figure 6-1 ).
At the time of this writing, these VHDL or Verilog-based flows account for around 95 percent of all ASIC and FPGA designs; however, there are a number of problems associated with these flows:
● Capturing the RTL is time-consuming : Even though Verilog and VHDL are intended to represent hardware, it is still time-consuming to use these languages to capture the functionality of a design.
● Verifying RTL is time-consuming : Using simulation to verify large designs represented in RTL is computationally expensive and time-consuming.
● Evaluating alternative implementations is difficult : Modifying and reverifying RTL to perform a series of what-if evaluations of alternative microarchitecture implementations is difficult and time-consuming. This means that the number of evaluations the design team can perform may be limited, which can result in a less-than-optimal implementation.
● Accommodating specification changes is difficult : If any changes to the specification are made during the course of the project, folding these changes into the RTL and performing any necessary reverification can be painful and time-consuming. This is a significant consideration in certain application areas, such as wireless projects, because broadcast standards and protocols are constantly evolving and changing.
● The RTL is implementation specific : Realizing a design in an FPGA typically requires a different RTL coding style from that used for an ASIC implementation. This means that it can be extremely difficult to retarget a complex design represented in RTL from one implementation technology to another. This is of concern when one is migrating an existing ASIC design into an FPGA equivalent or creating an FPGA design to be used as a prototype for a future ASIC implementation.
One way to view this is that all of the implementation intelligence associated with the design is hardcoded into the RTL, which therefore becomes implementation specific. It’s important to understand that this implementation specificity goes beyond the coarse ASIC-versus-FPGA boundary, which dictates that RTL intended for an FPGA implementation is not suitable for an optimal ASIC realization, and vice versa. Even assuming a single target device architecture, the way in which a set of algorithms is used to process data may require a number of different microarchitecture implementations, depending on the target application areas.
Actually, to be scrupulously fair, we should probably note that the same RTL may be used to drive both ASIC and FPGA implementations. The reason for doing this is to avoid the risk of introducing a functional bug into the RTL when retargeting the code, but there is typically a penalty to be paid. That is, if code originally targeted toward an FPGA implementation is subsequently used to drive an ASIC implementation, the resulting ASIC will typically require more silicon real estate and have higher power consumption as compared to using RTL created with an ASIC architecture in mind. Similarly, if code originally targeted toward an ASIC implementation is subsequently used to drive an FPGA implementation, the ensuing FPGA will typically take a significant performance hit as compared to using RTL created with an FPGA architecture in mind.
RTL is less than ideal for hardware-software codesign: System-on-chip (SoC) devices are generally understood to be those that include microprocessor cores. Irrespective of whether these designs are to be realized using ASICs or FPGAs, today’s SoCs are exhibiting an ever-increasing amount of software content. When coupled with increased design reuse on the hardware side, in many cases it is necessary to verify the software and hardware concurrently so as to completely validate such things as the system diagnostics, RTOS, device drivers, and embedded application software. Generally speaking, it can be painful verifying (simulating) the hardware represented in VHDL or Verilog in conjunction with the software represented in C/C ++ or assembly language.
One approach that addresses the issues enumerated above is to perform the initial design capture at a higher level of abstraction than can be achieved with RTL VHDL/Verilog. The first such level is to use some form of C/C ++ , but as usual nothing is simple because there are a variety of alternatives, including SystemC, augmented C/C ++ , and pure C/C ++ .
Before we leap into the fray, we should tie down a couple of points to ensure that we’re all marching in step to the same beat. First, there is a wide variety of programming languages available, but—excepting specialist application areas—the most commonly used by far are traditional C and its object-oriented offspring C ++ . For our purposes here, we will refer to these collectively as C/C ++ .
The next point of import is that, by default, statements in languages like C/C ++ are executed sequentially. For example, assuming that we have already declared three integer variables called a, b, and c, then the following statements:
a = 6; /* Statement in C/C + + program */
b = 2; /* Statement in C/C + + program */
c = 9; /* Statement in C/C + + program */
would, perhaps not surprisingly, occur one after the other. However, this has certain implications; for example, if we now assume that the following statements occur sometime later in the program:
a = b; /* Statement in C/C + + program */
b = a; /* Statement in C/C + + program */
then a (which initially contained 6) will be loaded with the value currently stored in b (which is 2). Next, b (which initially contained 2) will be loaded with the value currently stored in a (which is now 2), so both a and b will end up containing the same value. The sequential nature of programming languages is the way in which software engineers think. However, hardware design engineers have quite a different view of the world. Let’s assume that a piece of hardware contains two registers called a and b that are driven by a common clock signal. Let’s further assume that these registers have previously been loaded with values of 6 and 2, respectively. Finally, let’s assume that at some point in the HDL code, we see the following statements:
a = b; /* Statement in VHDL/Verilog Code */
b = a; /* Statement in VHDL/Verilog Code */
As usual, this syntax doesn’t actually represent VHDL or Verilog; it’s just a generic syntax used only for the purposes of this example. Generally speaking, hardware engineers would expect both of these statements to be executed concurrently (at the same time). This means that a (which initially contained 6) will be loaded with the value stored in b (which was 2) while—at the same time— b (which initially contained 2) will be loaded with the value stored in a (which was 6). The result is that the initial contents of a and b will be exchanged. As usual, of course, the above is something of a simplification. However, it’s fair to say that HDL statements will execute concurrently by default, unless sequential behavior is forced by means of techniques like blocking assignments. Thus, by default, RTL-based logic simulators will execute the statements shown above in this concurrent manner; similarly RTL-based logic synthesis tools will generate hardware that handles these two activities simultaneously. By comparison, unless explicitly directed to do otherwise (by means of the techniques introduced later in this chapter), C/C ++ statements will e xecute sequentially.
What exactly is SystemC (and where did it come from)?
Before we consider SystemC-based flows, it is probably a good idea to elaborate a bit more on just what SystemC is, because there is typically some confusion on this point . SystemC 1.0 – One of the underlying concepts behind SystemC is that it is an open-source environment to which everyone contributes. As an example, consider Linux, which was rough around the edges at first. Based on contributions from different folks, however, Linux eventually became a real operating system (OS) with the potential to challenge Microsoft. In this spirit, a relatively undocumented SystemC 1.0 was let loose to roam wild and free circa 2000. SystemC 1.0 was a C ++ class library that facilitated the representation of notions such as concurrency (things happening at the same time), timing, and I/O pins. By means of this class library, engineers could capture designs at the RTL level of abstraction.
One advantage of this early incarnation was that it facilitated hardware/software codesign environments. Another was that SystemC representations at the RTL level of abstraction might simulate 5 to 10 times faster than their VHDL and Verilog counterparts. On the downside, it was harder and more time-c onsuming to capture an RTL-level design in SystemC 1.0 than with VHDL or Verilog. Furthermore, there was a scarcity of design tools that could synthesize SystemC 1.0 representations into netlist-level equivalents with any degree of sophistication.
SystemC 2.0 – Later, in 2002, SystemC 2.0 arrived on the scene. This augmented the 1.0 release with some high-level modeling constructs such as FIFOs (a form of memory that can accept and subsequently make available a series of words of data and that operates on a first-in first-out principle). The 2.0 release also included a variety of behavioral, algorithmic, and system-level modeling capabilities, such as the concepts of transactions and channels (which are used to describe the communication of data between blocks at an abstract level).
To gain a little more perspective on SystemC, let’s first consider a typical scenario of how things would have worked using the original SystemC 1.0. As a simple example, let’s assume that we have two functions called f ( x ) and g ( x ) that have to communicate with each other ( Figure 6-2 ).
In this case, the interface between the blocks would have to be defined at the pin level. The real problem with this approach occurs when you are in the early stages of a design, because you are already defining implementation details such as bus widths. This makes things difficult to change if you wish to experiment with different what-if architectural scenarios. This aspect of things became much easier with SystemC 2.0, which allowed abstract interfaces to be declared between the blocks ( Figure 6-3 ).
Now, the interfacing between the blocks can be performed at the level of abstract records on the basis that, in the early stages of the design cycle, we don’t really care how data gets from point a to point b, just that it does get there somehow.
These abstract interfaces facilitate performing architectural evaluation early in the design cycle. Once the architecture starts to firm up, you can start refining the interface by using high-level constructs such as a FIFO to which one would assign attributes like width and depth and characteristics like blocking write, nonblocking read, and how to behave when empty or full. Still later, this logical interface can be replaced by a completely specified (pin-level) interface that binds the functional blocks together at a more physical level.
Truth to tell, this is where things start to become a little fuzzy around the edges, not the least because one runs into different definitions depending on to whom one is talking. As a first pass, however, we might take a stab at capturing the different levels of SystemC abstraction, as shown in Figure 6-4 .
This is why things become confusing, because SystemC can mean all things to all people. To some it’s a replacement for RTL VHDL/Verilog, while to others it’s a single language that can be used for system-level specification, algorithmic and architectural analysis, behavioral design, and testbenches for use in verification.
One area of confusion comes when you start to talk about behavioral synthesis. This encompasses certain aspects of both the algorithmic and transactional levels (in the latter case, however, you have to be careful as to how to define your transactions).
This is a tricky one because one might go various ways here.
● Many of today’s designs begin life as complex algorithms. In this case, it is very common to start by creating a C or C ++ representation. This representation can be used to validate the algorithms by compiling it into a form that can be run (simulated) 1,000 or more times faster than an RTL equivalent. In the case of the HDL-based flows discussed in Chapter 5, this C/C ++ representation of the algorithms would then be hand-translated into RTL VHDL/Verilog. The C/C ++ representation will typically continue to be used as a golden model, which means it can be linked into the RTL simulator and run in parallel with the RTL simulation. The results from the C/ C ++ and RTL models can be compared so as to ensure that they are functionally equivalent.
● Alternatively, in one flavor of a SystemC-based flow, the original C/C ++model could be incrementally modified by adding timing, concurrency, pin definitions, and so forth to transform it to a level at which it would be amenable to SystemC-based RTL or behavioral synthesis.
● In another flavor of a SystemC-based flow, the design might be initially captured in SystemC using system, algorithmic, or transaction-level constructs that could be used for verification at a high level of abstraction. This representation could then be incrementally modified to bring it down to a level at which it would be amenable to SystemC-based RTL or behavioral synthesis.
Irrespective of the actual route by which one might get there, let’s assume that we are in possession of a SystemC representation of a design that is suitable for SystemC-based behavioral or RTL synthesis. In this case, there are two main design-flow alternatives, which are:
to translate the System C into RTL VHDL/Verilog automatically and then to use conventional RTL synthesis technology, or
2. to use SystemC-based synthesis to generate an implementation-level netlist directly.
—Technology Trade-offs—
● There are two schools of thought here. One says that synthesizing the SystemC directly into the implementation-level netlist offers the cleanest, fastest, and most efficient route.
● Another view is that it’s better to translate the SystemC into RTL VHDL/ Verilog first because RTL is the way design engineers really visualize their world; that this level is a natural staging point for integrating design blocks (including third-party IP) originating from multiple sources; and that Verilog/VHDL synthesis technology is extremely mature and powerful (as compared to SystemC-based synthesis technology).
Both of these flows can be applied to ASIC or FPGA targets ( Figure 6-5 ).
The first SystemC synthesis applications were predominantly geared toward ASIC flows, so they didn’t do a very good job at inferring FPGAs pecific entities such as embedded RAMs, embedded multipliers, and so forth. More recent incarnations do a much better job of this, but the level of sophistication exhibited by different tools is a moving target, so the prospective user is strongly advised to perform some indepth evaluations before slapping a bundle of cash onto the bargaining table.
Note that Figure 6-5 shows the use of implementation specific SystemC to drive the ASIC versus FPGA flows. As soon as you start coding at the RTL level and adding timing concepts, be it in VHDL, Verilog, or SystemC, then achieving an optimal implementation requires that the code be written with a specific target architecture in mind.
—Technology Trade-offs—
● Once again, having said this, the same SystemC can be used to drive both ASIC and FPGA flows, but there is typically a penalty to be paid. If SystemC code originally targeted toward an FPGA implementation is subsequently used to drive an ASIC flow, the resulting ASIC will typically require more silicon real estate and have higher power consumption as compared to using code created with an ASIC architecture in mind.
● Similarly, if code originally targeted toward an ASIC implementation is subsequently used to drive an FPGA flow, the ensuing FPGA will typically take a significant performance hit as compared to using code created with an FPGA architecture in mind. This is primarily a result of hard-coding the microarchitecture definition in the source.
Depending on who you are talking to, folks either love SystemC or they loath it. Most would agree that SystemC 2.0 is very promising and that there’s no other language that provides the same capabilities (some of these capabilities are being added into SystemVerilog, but not all of them).
On the downside, many design engineers are reasonably proficient at writing C, but most of them are significantly less familiar with the object-oriented aspects of C ++. So requiring them to use SystemC means giving them more power on the one hand, while thrusting them into a world they don’t like or understand on the other. It’s also true that while SystemC can be very useful for verification and highlevel system modeling, in some respects it’s still relatively immature toolwise with regard to actual implementation flows.
One school of thought says that, although SystemC is difficult to write by hand and also difficult to synthesize, which makes it a somewhat clumsy specification language, it does provide a powerful framework for simulation across languages and levels of abstraction. At the time of this writing, a number of companies that were strong supporters of SystemC in the United States have grown somewhat less vocal over the last few years. On the other hand, SystemC is gaining some ground in Europe and Asia. What does the future hold? Wait a few years, and I’ll be happy to tell you!
What do we mean by augmented C/C ++?
There are two ways in which standard C/C ++ can be augmented to extend its capabilities and the things it can be used to represent. The first is to include special comments, known as commented directives or pragmas, into the pure C/C ++ code. These comments can subsequently be recognized and interpreted by parsers, precompilers, compilers, and other tools and used to add constructs to the code or modify the way in which it is processed. One significant drawback to this approach is that simulation requires the use of proprietary C/C ++ compilers as opposed to using standard off-the-shelf compilers. This limits the options customers have and is only viable if standards are developed for multiple EDA vendors to leverage.
The other way in which C/C++ can be augmented is to add special keywords and statements into the language. This is a very popular technique, and there is a veritable plethora of such language variants roaming wild and free around the world, each tailored toward a different application area. One downside of this approach is that, once again, it requires proprietary C/C ++ compilers; otherwise, tools such as simulators that have not been enhanced to understand these new keywords and statements will crash and burn. A common solution to this problem is to wrap standard #ifdef directives around the new keywords and statements such that a precompiler can be used to discard them as required (this is somewhat inelegant, but it works).
In the case of capturing the functionality of hardware for ASIC and FPGA designs, it is necessary to augment standard C/C ++ with special statements to support such concepts as clocks, pins, concurrency, synchronization, and resource sharing.
Assuming that you have an initial model represented in pure C/C ++ , the first step would be to augment it with clock statements, along with interface statements used to define the input and output pins. You could then use an appropriate synthesis tool to generate an implementation (as discussed below).
However, because C/C ++ is by nature sequential, the resulting hardware can be horribly slow and inefficient if the synthesis tool is not capable of locating potential parallelisms and exploiting them. For example, assume that we have the following statements in a C/C ++representation of the design:
a = 6; /* Standard C/C + + statement */
b = 2; /* Standard C/C + + statement */
c = 9; /* Standard C/C + + statement */
d = a + b; /* Standard C/C + + statement */
:
etc
By default, each sign is assumed by the synthesis application to represent one clock cycle. Thus, if the above code were left as is, the augmented C/C ++ synthesis tool would generate hardware that loaded variable (register) a with 6 on the first clock, then b with 2 on the next clock, then c with 9 on the next clock, and so forth. Thus, by hardware standards, this would run horribly slowly.
Of course, most synthesis tools would be capable of locating and exploiting the potential parallelisms in the above example, but they might well miss more complex cases that require human consideration and intervention. For the purposes of these discussions, however, we shall continue to work with this simple test case. The point is that an augmented C/C ++ language will have keywords like “ parallel ” (or “ par ” ) and “ sequential ” (or “ seq ” ) that will instruct the downstream synthesis application as to which statements should be executed in parallel, and so forth. For example:
parallel; /* Augmented C/C + + statement */
a = 6; /* Standard C/C + + statement */
b = 2; /* Standard C/C + + statement */
c = 9; /* Standard C/C + + statement */ sequential; /* Augmented C/C + + statement */
d = a b; /* Standard C/C + + statement */
:
etc
In this case, the parallel statement instructs the synthesis tool that the following statements can be implemented concurrently, while the sequential statement implies that the preceding operations must occur prior to any subsequent actions taking place. Of course, these parallel and sequential statements can be nested as required.
Things become more complex in the case of loops, depending on whether the designer wishes to unravel them partially or fully. Just to give a point of reference, we might visualize a loop as being something like “ for i = 1 to 10 in increments of 1 do xxxx, yyyy, and zzzz ” . In some cases, it may be possible to simply associate a parallel or sequential statement with the loop, but if more subtlety is required, the designer may be obliged to completely rewrite these constructs. It may also be necessary to add “ share ” statements if resource sharing is required, and “ channel ” statements to share signals between expressions, and the list goes on.
As usual, one might go various ways here. As we previously discussed, in the case of a design that begins life as a suite of algorithms, it is very common to start by creating a C or C ++ representation. Following verification, this C/C ++ model can be incrementally modified by adding statements for clocks, pins, concurrency, synchronization, and resource sharing so as to make the model suitable for the appropriate synthesis utility. Alternatively, the design might be captured using the augmented C/C ++ language from the get-go. Irrespective of the actual route we might take to get there, let’s assume that we are in possession of an augmented C/C ++ representation of a design that is suitable for synthesis. Once again, there are two main design-flow alternatives, which are (1) to translate the augmented C/C ++ into Verilog or VHDL at the RTL level of abstraction automatically and to then use conventional RTL synthesis technology, or (2) to use an appropriate augmented C/C ++ synthesis engine.
And, once again, one school of thought says that synthesizing the augmented C/C ++ directly into the implementation level netlist offers the cleanest, fastest, and most efficient route. Others say that the RTL Verilog/VHDL level is the natural staging post for design integration and that today’s RTL synthesis technology is extremely mature and powerful. Both of these flows can be applied to ASIC or FPGA targets ( Figure 6-6 ). The first augmented C/C++ synthesis applications were predominantly geared toward ASIC flows. This meant that these early incarnations didn’t do a tremendous job when it came to inferring FPGA-specific entities such as embedded RAMs, embedded multipliers, and so forth. More recent versions of these tools do a much better job at this, but, as usual, the prospective user is strongly advised to perform some in-depth evaluations before handing over any hard-earned cash. Note that Figure 6-6 shows the use of implementation-specific code to drive the ASIC versus FPGA flows because achieving an optimal implementation requires that the code be written with a specific target architecture in mind. In reality, the same code can be used to drive both ASIC and FPGA flows, but there is usually a penalty to be paid (see the discussions on SystemC for more details).
Last, but not least, we come to pure C/C ++ -based flows. In reality, the term pure C/C++ actually refers to industry-standard C/C ++ that is minimally augmented with SystemC data types to allow specific bit widths to be associated with variables and constants.
Although relatively new, pure C/C ++ -based flows offer a number of advantages as compared to other C-based flows and traditional Verilog-/VHDL-based flows:
● Creating pure C/C++ is fast and efficient : Pure untimed C/C ++ representations are more compact and easier to create and understand than equivalent SystemC and augmented C/C ++ representations (and they are much more compact than their RTL equivalents, requiring perhaps 1/10th to 1/100th of the code).
● Verifying C/C++ is fast and efficient : A pure untimed C/C ++ representation will simulate significantly faster than a timed SystemC or augmented C/C ++ model and 100 to 10,000 times faster than an equivalent RTL representation. In fact, pure C/C ++ models are already widely created and used by system designers for algorithm and system validation.
● Evaluating alternative implementations is fast and efficient : Modifying and reverifying pure untimed C/C ++ to perform a series of what-if evaluations of alternative microarchitecture implementations is fast and efficient. This facilitates the design team’s ability to arrive at fundamentally superior microarchitecture solutions. In turn, this can result in significantly smaller and faster designs as compared to flows based on traditional hand-coded RTL methods.
● Accommodating specification changes is relatively easy : If any changes to the specification are made during the course of the project, it’s relatively easy to implement and evaluate these changes in a pure untimed C/C ++representation, thereby allowing the changes to be folded into the resulting implementation.
Furthermore, as noted earlier in this chapter, one of the most significant problems associated with existing SystemC and augmented C/C ++ -based design flows is that the implementation intelligence associated with the design has to be hard-coded into the model, which therefore becomes implementation specific.
A key aspect associated with a pure untimed C/C ++ -based design flow is that the code presented to the synthesis engine is just what someone would write if he or she didn’t have any preconceived hardware implementation or target device architecture in mind. This means that the C/C ++ code that system designers write today is an ideal input to this form of synthesis. The only modification typically required to use a pure C/C ++ model with the synthesis engine is to add a single special comment to the source code to indicate the top of the functional portion of the design (anything conceptually above this point is considered to form part of the testbench).
As opposed to adding intelligence to the source code (thereby locking it into a target implementation), all of the intelligence is provided by the user controlling and guiding the synthesis engine itself ( Figure 6-7 ).
Once the synthesis engine has parsed the source code, the user can use it to perform microarchitecture trade-offs and evaluate their effects in terms of size and speed. The synthesis engine analyzes the code, identifies its various constructs and operators, along with their associated data and memory dependencies, and automatically provides for parallelism wherever possible. The engine also provides a graphical interface that allows the user to specify how different elements should be handled. For example, the interface
● allows the user to associate ports with registers or RAM blocks;
● identifies constructs like loops and allows the user to specify on an individual basis whether they should be fully unraveled, partially unraveled, or left alone;
● allows the user to specify whether loops and other constructs should be pipelined;
● allows the user to perform resource sharing on specific entities;
● and so forth.
These evaluations are performed on the fly, and the synthesis engine reports total size/area and latency in terms of clock cycles and I/O delays (or through put time/cycles in the case of pipelined designs). The user-defined configuration associated with each what-if scenario can be named, saved, and reused as required (it would be almost impossible to perform these trade-offs in a timely manner using a conventional hand-coded RTL-based flow).
Once the user’s evaluations are completed, clicking the “ Go ” button causes the synthesis engine to generate corresponding RTL VHDL. This code can subsequently be used by conventional logic synthesis or physically aware synthesis applications to generate the netlist used to drive the downstream implementation (place-and-route, etc.) tools.
As usual, it would be possible to synthesize the pure untimed C/C ++ directly into a gate-level netlist (this alternative is not shown in Figure 6-7 ). However, generating the intermediate RTL provides a comfort zone for the engineers by allowing them to check that they are satisfied with the implementation decisions that have been made during the course of the C/C ++ to RTL translation. Furthermore, generating intermediate RTL is useful because this is the level of abstraction where hardware design engineers generally stitch together the various functional blocks forming their designs. Large portions of today’s designs are typically presented in the form of IP blocks represented in RTL. This means that the intermediate RTL step shown in Figure 6-7 is a useful point in the design flow for integrating and verifying the entire hardware system. The design engineers can then take full advantage of their existing RTL synthesis technology, which is mature, robust, and well understood.
The fundamental difference between the various C/C ++ -based flows presented in this chapter is the level of synthesis abstraction each can support. For example, although SystemC offers significant system-level, algorithmic, and transaction-level modeling capabilities, its synthesizable subset is at a relatively low level of abstraction. Similarly, although augmented C/C ++ representations are closer to pure C/C ++ than are their SystemC counterparts, which means that they simulate much more quickly, their synthesizable subset remains significantly lower than would be ideal.
This lack of synthesis abstraction causes the timed SystemC and augmented C/C ++ representations to be implementation specific. In turn, this makes them difficult to create and modify and significantly reduces their flexibility with regard to performing what-if evaluations and retargeting them toward alternative implementation technologies ( Figure 6-8 ).
By comparison, the latest generation of pure untimed C/C ++ synthesis technology supports a high level of synthesis abstraction. Non-i mplementationspecific C/C ++ models are very compact and can be quickly and easily created and modified. By means of the synthesis engine itself, the user can quickly and easily perform what-if evaluations and retarget the design toward alternative implementation technologies. The result is that a pure C/C ++ - based design flow can dramatically speed implementation and increase design flexibility as compared to other C/C ++ -based flows.
Before anyone starts to pen irate letters claiming the author is anti-SystemC, it should be reiterated that the discussions presented here are focused on the use of the various flavors of C/C ++ in the context of FPGA implementation flows. In this case, the tool-chain used to progress SystemC representations through to actual implementations is relatively immature and unsophisticated. When it comes to system-level modeling and verification applications, however, SystemC can be extremely efficacious (many users see SystemC and SystemVerilog being used in conjunction with each other, with SystemC being employed for the initial system-level design representation, and then SystemVerilog being used to “ flesh out ” the implementation-level details.
Last, but not least, we should note that a number of EDA companies can provide mixed-level design and verification environments that can support the cosimulation of models specified at multiple levels of abstraction.
In some cases, this may simply involve linking a C/C ++ model to a Verilog simulator via its programming language interface (PLI) or to a VHDL simulator via its foreign language interface (FLI). Alternatively, one might find a SystemC environment with the capability to accept blocks represented in Verilog or VHDL.
And then there are very sophisticated environments that start with a graphical block-based editor showing the design’s major functional units, where the contents of each block can be represented using the following:
● VHDL
● Verilog
● SystemVerilog
● SystemC
● Handel-C
● Pure C/C ++
The top-level design might be in a traditional HDL that calls submodules in the various HDLs and in one or more flavors of C/C ++ . Alternatively, the t op-level design might be in one of the flavors of C/C ++ that calls submodules in the various languages.
In this type of environment, the VHDL, Verilog, and SystemVerilog representations are usually handled by a single-kernel simulation engine. This engine is then cosimulated with appropriate engines for the various flavors of C/C ++ . Furthermore, this type of environment will incorporate source-code debuggers that support the various flavors of C/C ++ ; it will allow testbenches to be created using any of the languages; and supporting tools like graphical waveform displays will be capable of displaying signals and variables associated with any of the language blocks.
In reality, the various mixed-language design and verification environment solution combinations and permutations change on an almost weekly basis, so you need to take a good look at what’s out there before you leap into the fray.
Manufacturer:Xilinx
Product Categories: FPGAs (Field Programmable Gate Array)
Lifecycle:Active Active
RoHS:
Manufacturer:Xilinx
Product Categories: CPLDs
Lifecycle:Active Active
RoHS: No RoHS
Manufacturer:Xilinx
Product Categories: Embedded - CPLDs (Complex Programmable Logic Devices)
Lifecycle:Active Active
RoHS: No RoHS
Manufacturer:Xilinx
Product Categories: FPGAs
Lifecycle:Active Active
RoHS:
Manufacturer:Xilinx
Product Categories:
Lifecycle:Obsolete -
RoHS: No RoHS
Support