FONT SIZE : AAA
We are concerned only with electronic systems that include one or more FPGAs on the printed circuit board (PCB). The vast majority of such systems also make use of a general-purpose microprocessor, or μ P, to perform a variety of control and data-processing applications. This is often referred to as the central processing unit (CPU) or microprocessor unit (MPU).
Until recently, the CPU and its peripherals typically appeared in the form of discrete chips on the circuit board. There are an almost infinite number of possible scenarios here, but the two main ones involve the way in which the CPU is connected to its memory ( Figure 6-17 ).
In both of these scenarios, the CPU is connected to an FPGA and some other stuff via a general-purpose processor bus. (By “ stuff ” we predominantly mean peripheral devices such as counter timers, interrupt controllers, communications devices, etc.)
In some cases, the main memory (MEM) will also be connected to the CPU by means of the main processor bus, as shown in Figure 6-17a (actually, this connection will be via a special peripheral called a memory controller, which is not shown here because we’re trying to keep things simple). Alternatively, the memory may be connected directly to the CPU by means of a dedicated memory bus, as shown in Figure 6-17b .
The point is that presenting the CPU and its various peripheral devices in the form of dedicated chips on the circuit board costs money and occupies real estate. It also impacts the reliability of the board because every solder joint (connection point) is a potential failure mechanism.
One alternative is to embed the CPU along with some of its peripherals in the FPGA itself ( Figure 6-18 ).
It is common for a relatively small amount of memory used by the CPU to be included locally in the FPGA. At the time of this writing, however, it is rare for all of the CPU’s memory to be included in the FPGA. Creating an FPGA design of this type brings a whole slew of new problems to the table:
● First, the system architects have to decide which functions will be implemented in software (as instructions to be executed by the CPU) and which functions will be implemented in hardware (using the main FPGA fabric).
● Next, the design environment must support the concept of coverification, in which the hardware and embedded software portions of the system can be verified together to ensure that everything works as it should. Both of these topics are considered in more detail later in this chapter.
As defined previously, a hard microprocessor core is one that is implemented as a dedicated, predefined (hardwired) block (these cores are only available in certain device families). Each of the main FPGA vendors has opted for a particular processor type to implement its hard cores. For example, Altera offers embedded ARM processors, QuickLogic has opted for MIPS-based solutions, and Xilinx sports PowerPC cores.
Of course, each vendor will be delighted to explain at great length why its implementation is far superior to any of the others (the problem of deciding which one actually is better is only compounded by the fact that different processors may be better suited to different tasks).
As noted in Chapter 2, there are two main approaches for integrating such cores into the FPGA. The first is to locate it in a strip to the side of the main FPGA fabric ( Figure 6-19 ). In this scenario, all of the components are typically formed on the same silicon chip, although they could also be formed on two chips and packaged as a multichip module (MCM).
One advantage of this implementation is that the main FPGA fabric is identical for devices with and without the embedded microprocessor core, which can make things easier for the design tools used by the engineers. The other advantage is that the FPGA vendor can bundle a whole load of additional functions in the strip to complement the microprocessor core, such as memory and special peripherals.
The second alternative is to embed one or more microprocessor cores directly into the main FPGA fabric . One, two, and even four core implementations are currently available at the time of this writing ( Figure 6-20 ).
In this case, the design tools have to be able to take account of the presence of these blocks in the fabric; any memory used by the core is formed from embedded RAM blocks, and any peripheral functions are formed from groups of general-purpose programmable logic blocks. Proponents of this scheme can argue that there are inherent speed advantages to be gained from having the microprocessor core in intimate proximity to the main FPGA fabric.
As opposed to embedding a microprocessor physically into the fabric of the chip, it is possible to configure a group of programmable logic blocks to act as a microprocessor. These are typically called “ soft cores, ” but they may be more precisely categorized as either soft or firm, depending on the way in which the microprocessor’s functionality is mapped onto the logic blocks. For example, if the core is provided in the form of an RTL netlist that will be synthesized with the other logic, then this truly is a soft implementation. Alternatively, if the core is presented in the form of a placed and routed block of LUTs/CLBs, then this would typically be considered a firm implementation.
In both of these cases, all of the peripheral devices like counter timers, interrupt controllers, memory controllers, communications functions, and so forth are also implemented as soft or firm cores (the FPGA vendors are typically able to supply a large library of such cores).
—Technology Trade-offs—
● Soft cores are slower and simpler than their hard-core counterparts (of course they are still incredibly fast in human terms). However, in addition to being practically free, they also have the advantages that you only have to implement a core if you need it and that you can instantiate as many cores as you require until you run out of resources in the form of programmable logic blocks.
Once again, each of the main FPGA vendors has opted for a particular processor type to implement its soft cores. For example, Altera offers the Nios, while Xilinx sports the MicroBlaze. The Nios has both 16-bit and 32-bit architectural variants, which operate on 16-bit or 32-bit chunks of data, respectively (both variants share the same 16-bit-wide instruction set). By comparison, the MicroBlaze is a true 32-bit machine (that is, it has 32-bit-wide instruction words and performs its magic on 32-bit chunks of data). Once again, each vendor will be more than happy to tell you why its soft core rules and how its competitors ’ offerings fail to make the grade (sorry, you’re on your own here).
One cool thing about the integrated development environment (IDE) fielded by Xilinx is that it treats the PowerPC hard core and the MicroBlaze soft core identically. This includes both processors being based on the same CoreConnect processor bus and sharing common soft peripheral IP cores. All of this makes it relatively easy to migrate from one processor to the other.
Also of interest is the fact that Xilinx offers a small 8-bit soft core called the PicoBlaze, which can be implemented using only 150 logic cells (give or take a handful). By comparison, the MicroBlaze requires around 1,000 logic cells (which is still extremely reasonable for a 32-bit processor implementation, especially when one is playing with FPGAs that can contain 70,000 or more such cells).
Some cynics say that those aspects of a design that are well understood are implemented in hardware, while any portions of the design that are somewhat undefined at the beginning of the design process are often relegated to a software realization (on the basis that the software can be tweaked right up until the last minute).
As noted in Chapter 2, almost any portion of an electronic design can be realized in hardware (using logic gates and registers, etc.) or software (as instructions to be executed on a microprocessor). One of the main partitioning criteria is how fast you wish the various functions to perform their tasks:
● Picosecond and nanosecond logic : This has to run insanely fast, which mandates that it be implemented in hardware (in the FPGA fabric).
● Microsecond logic : This is reasonably fast and can be implemented either in hardware or software (this type of logic is where you spend the bulk of your time deciding which way to go).
● Millisecond logic : This is the logic used to implement interfaces such as reading switch positions and flashing light-emitting diodes, or LEDs. It’s a pain slowing the hardware down to implement this sort of function (using huge counters to generate delays, for example). Thus, it’s often better to implement these tasks as microprocessor code (because processors give you lousy speed—compared to dedicated hardware—but fantastic complexity).
The trick is to solve every problem in the most cost-effective way. Certain functions belong in hardware, others cry out for a software realization, and some functions can go either way depending on how you feel you can best use the resources (both chip-level resources and hardware/software engineers) available to you.
It is possible to envisage an “ ideal ” electronic system level (ESL) environment in which the system architects initially capture the design via a graphical interface as a collection of functional blocks that are connected together. Each of these blocks could then be provided with a system-/algorithmic level SystemC representation, for example, and the entire design could be verified prior to any decisions being made as to which portions of the design were to be implemented in hardware and software.
When it comes to the partitioning process itself, we might dream of having the ability to tag each graphical block with the mouse and select a hardware or software option for its implementation. All we would then have to do would be to click the “ Go ” button, and the environment would take care of synthesizing the hardware, compiling the software, and pulling everything together.
And then we return to the real world with a resounding thud. Actually, a number of next-generation design environments show promise, and new tools and techniques are arriving on an almost daily basis. At the time of this writing, however, it is still very common for system architects to partition a design into its hardware and software portions by hand, and to then pass these top-level functions over to the appropriate engineers and hope for the best.
With regard to the software portion of the design, this might be something as simple as a state machine used to control a human-level interface (reading the state of switches and controlling display devices). Although the state machine itself may be quite tricky, this level of software is certainly not rocket science. At the other end of the spectrum, one might have incredibly complex software requirements, including:
● System initialization routines and a hardware abstraction layer
● A hardware diagnostic test suite
● A real-time operating system (RTOS)
● RTOS device drivers
● Any embedded application code
This code will typically be captured in C/C ++ and then compiled down to the machine instructions that will be run on the processor core (in extreme cases where one is attempting to squeeze the last drop of performance out of the design, certain routines may be handcrafted in assembly code). At the same time, the hardware design engineers will typically be capturing their portions of the design at the RTL level of abstraction using VHDL or Verilog (or SystemVerilog). Today’s designs are so complex that their hardware and software portions have to be verified together.
By comparison, the software guys and gals think in terms of C/C++ source code, of registers in the CPU (and in the peripherals), and of the contents of various memory locations. When software engineers are debugging a program, they often wish to single-step through the code one line at a time and watch the values in the various registers changing. Or they might wish to set one or more breakpoints (this refers to placing markers at specific points in the code), run the program until they hit one of those breakpoints, and then pause to see what’s going on. Alternatively, they might wish to specify certain conditions such as a register containing a particular value, then run the program until this condition is met, and once again pause to see what’s happening.
When a software developer is writing application code such as a game, he or she has the luxury of being reasonably confident that the hardware (say, a home computer) is reasonably robust and bug-free. However, it’s a different ball game when one is talking about a software engineer creating embedded applications intended to run on hardware that’s being designed at the same time. When a problem occurs, it can be mega tricky determining if it was a fault in the software or if the hardware was to blame.
Perhaps the simplest place to start is the scenario where the FPGA is used as its own development environment. The idea here is that you have an SRAMbased FPGA with an embedded processor (hard or soft) mounted on a development board that’s connected to your computer. In addition to the FPGA, this development board will also have a memory device that will be used to store the software programs that are to be run by the embedded CPU ( Figure 6-21 ).
Once the system architects have determined which portions of the design are to be implemented in hardware and software, the hardware engineers start to capture their RTL blocks and functions and synthesize them down to a LUT/CLB-level netlist. Meanwhile, the software engineers start to capture their C/C ++ programs and routines and compile them down to machine code. Eventually, the LUT/CLB-level netlist will be loaded into the FPGA via a configuration file, the linked machine code image will be loaded into the memory device, and then you let the system run wild and free ( Figure 6-22 ).
Also, any of the machine code that is to be embedded in the FPGA’s onchip RAM blocks would actually be loaded via the configuration file. Improving Visibility in the Design The main problem with the scenario discussed in the previous section is lack of “ visibility ” as to what is happening in the hardware portion of the design.
One way to mitigate this is to use a virtual logic analyzer to observe what’s happening in the hardware.
Things can be a little trickier when it comes to determining what’s h appening with the software. One point to remember is that—as discussed in Chapter 3— an embedded CPU core will have its own dedicated JTAG boundary scan chain ( Figure 6-23 ).
This is true of both hard cores and the more sophisticated soft cores. In this case, the coverification environment can use the scan chain to monitor the activity on the buses and control signals connecting the CPU to the rest of the system. The CPU’s internal registers can also be accessed via the JTAG port, thereby allowing an external debugger to take control of the device and singlestep through instructions, set breakpoints, and so forth. A Few Coverififi cation Alternatives If you really want to get visibility into what’s happening in the hardware portions of design, one approach is to use a logic simulator. In this case, the majority of the system will be modeled and simulated in VHDL or Verilog/ SystemVerilog at the RTL level of abstraction. When it comes to the CPU core, however, there are various ways in which to represent this ( Figure 6-24 ).
Irrespective of the type of model used to represent the CPU, the embedded software (machine code) portion of the design will be loaded into some form of memory—either embedded memory in the FPGA or external memory devices—and the CPU model will then execute those machine code instructions.
Note that Figure 6-24 shows a high-level representation of the contents of the FPGA only. If the machine code is to be stored in external memory devices, then these devices would also have to be part of the simulation.
In fact, as a general rule of thumb, if the software talks to any stuff, then that stuff needs to be part of the coverification environment.
Perhaps the simplest option here is when one has an RTL model of the CPU, in which case all of the activity takes place in the logic simulator. One disadvantage of this approach is that a CPU performs tremendous numbers of internal operations in order to perform the simplest task, which equates to incredibly slow simulation runs (you’ll be lucky to be able to simulate 10 to 20 system clocks per second in real time).
The other disadvantage is that you have no visibility into what the software is doing at the source code level. All you’ll be able to do is to observe logic values changing on wires and inside registers.
And there’s always the fact that whoever supplies the real CPU doesn’t want you to know how it works internally because that supplier may be using cunning proprietary tricks and wish to preserve their IP. In this case, you may well find it very difficult to lay your hands on an RTL model of the CPU at all.
As opposed to using an RTL model, it is very common to have access to some sort of C/C ++ model of the CPU. (The proponents of SystemC have a vision of a world in which the CPU and the main peripheral devices all have SystemC models provided as standard for use in this type of design environment.)
The compiled version of this CPU model would be linked into the simulation via the programming language interface (PLI) in the case of a Verilog simulator or the foreign language interface (FLI)—or equivalent—in the case of a VHDL simulator.
The advantages of such a model are that it will run much faster than its RTL counterpart; that it can be delivered in compiled form, thereby preserving any secret IP; and that, at least in FPGA circles, such a model is usually provided for free (the FPGA vendors are trying to sell chips, not models).
One disadvantage of this approach is that the C/C ++ model may not provide a 100-percent cycle-accurate representation of the CPU, which has the potential to cause problems if you aren’t careful. But, once again, the main disadvantage of such a model is that its only purpose is to provide an engine to execute the machine code program, which means that you have no visibility into what the software is doing at the source code level. All you’ll be able to do is observe logic values changing on wires and inside registers.
Yet another possibility is to use a physical device to represent a hard CPU core. For example, if you are using a PowerPC core in a Xilinx FPGA, you can e asily lay your hands on a real PowerPC chip. This chip can be installed in a box called a hardware modeler, which can then be linked into the logic simulation system. The advantage of this approach is that you know the physical model (chip) is going to functionally match your hard core as closely as possible. Some disadvantages are that hardware modelers aren’t cheap and they can be a pain to use.
The majority of hardware-modeler-based solutions don’t support sourcelevel debugging, which, once again, means that you have no visibility into what the software is doing at the source code level. All you’ll be able to do is to observe logic values changing on wires and inside registers.
As previously noted, in certain cases, the role of the software portion of a design may be somewhat limited. For example, the software may be acting as a state machine used to control some interface. Alternatively, the software’s role may be to initialize certain aspects of the hardware and then sit back and watch the hardware do all of the work. If this is the case, then a C/C ++ model or a physical model is probably sufficient—at least as far as the hardware design engineer is concerned.
At the other extreme, the hardware portions of the design may exist mainly to act as an interface with the outside world. For example, the hardware may read in a packet of data and store it in the FPGA’s memory, and then the CPU may perform huge amounts of complex processing on this data. In cases like these, it is necessary for the software engineer to have sophisticated sourcelevel debugging capabilities. This requires the use of an instruction set simulator (ISS), which provides a virtual representation of the CPU.
Although an ISS will almost certainly be created in C/C ++ , it will be architected very differently from the C/C ++ models of the CPU discussed earlier in this section. This is because the ISS is created at a very high level of abstraction; it thinks in terms of transactions like “ get me a word of data from location x in the memory, ” and it doesn’t concern itself with details like how signals will behave in the real world.
The easiest way to explain how this works is by means of an illustration ( Figure 6-25 ).
First, the software engineers capture their program as C/C++ source code. This is then compiled using the -d (debug) option, which generates a symbol table and other debug-specific information along with the executable machine code image.
When we come to perform the coverification, there are a number of pieces to the puzzle. At one end we have the source-level debugger, whose interface is used by the software engineer to talk to the environment. At the other end we have the logic s imulator, which is simulating representations of the memory, stuff like peripheral devices, general-purpose logic, and so forth (for the sake of simplicity, this illustration assumes that all of the program memory resides in the FPGA itself).
In the case of the CPU, however, the logic simulator essentially sees a hole where this function should be. To be more precise, the simulator actually sees a set of inputs and outputs corresponding to the CPU. These inputs and outputs are connected to an entity called a bus interface model (BIM), which acts as a translator between the simulator and the ISS.
Both the source code and the executable image (along with the symbol table and other debug-centric information) are loaded into the source-level debugger. At the same time, the executable image is loaded into the MEM block. When the user requests the source-level debugger to perform an action like stepping through a line of source code, it issues commands to the ISS. In turn, the ISS will execute high-level transactions such as an instruction fetch, or a memory read/write, or an I/O command. These transactions are passed to the BIM, which causes the appropriate pins to “ wiggle ” in the simulation world.
Similarly, when something connected to the processor bus in the FPGA attempts to talk to the CPU, it will cause the pins driving the BIM to “ wriggle. ” The BIM will translate these low-level actions into high-level transactions that it passes to the ISS, which will in turn inform the source-level debugger what’s happening. The source-level debugger will then display the state of the program variables, the CPU registers, and other information of this ilk.
There are a variety of incredibly sophisticated (often frighteningly expensive) environments of this type on the market. Each has its own cunning tricks and capabilities, and some are more appropriate for ASIC designs than FPGAs or vice versa. As usual, however, this is a moving target, so you need to check around to see who is doing what before putting any of your precious money on the table.
Manufacturer:Xilinx
Product Categories: FPGAs
Lifecycle:Active Active
RoHS:
Manufacturer:Xilinx
Product Categories: FPGAs
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
Manufacturer:Xilinx
Product Categories: Embedded - CPLDs (Complex Programmable Logic Devices)
Lifecycle:Active Active
RoHS:
Support