21 Miscellaneous Topics

In the preceding chapters we have introduced most of the facilities provided by VHDL and shown how they may be used to model a variety of hardware systems at various levels of detail. However, there remain a few VHDL facilities that we have not yet discussed. In this chapter, we tie off these loose ends.

21.1 Buffer and Linkage Ports

When we introduced ports in Chapter 5, we identified three modes, in, out and inout, that control how data is passed to and from a design entity. VHDL provides two further modes, buffer and linkage. These modes may only be specified for ports of entities, blocks and components, not for generic constants or subprogram parameters.

A buffer mode port behaves in a similar way to an inout mode port, in that the port can be both read and assigned in the entity or block. The source of a buffer port determines the driving value of the port in the normal way. However, when the port is read, the value read is the driving value. This behavior differs from inout ports in that the value read for an inout port may be different from the driving value if the actual signal associated with the port is resolved. The behavior of a buffer port allows us to model a design that has a buffered output connection and internally uses the value driving the buffer. In this case we do not explicitly represent the buffer as a component, nor its input as a signal.

EXAMPLE    

We can implement a counter as a string of flipflops, with each stage feeding the next. This form of counter is usually called a “ripple" counter. An entity declaration for a simple two-bit counter is shown in Figure 21-1. The corresponding architecture body drives the output ports q0 and q1 with the q outputs of the flipflops bit0 and bit1. Ports q0 and q1 are also used as the inputs to the inverters inv0 and invl. If q0 and q1 were declared as ports of mode out, we could not make these internal connections. Since we really want q0 and q1 to be outputs, we declare them to be of mode buffer, allowing us to read the ports internally as well as treating them as outputs.

FIGURE 21-1 An entity and architecture body for a two-bit ripple counter.

VHDL-87 AND VHDL-93

VHDL-87 and VHDL-93 impose a number of restrictions on how buffer ports may be interconnected with other ports. First, if the actual object associated with a buffer port of a component instance is a port of the enclosing entity, it must also be a buffer port. Second, if we associate a buffer port as an actual object with some formal port of a component instance, the formal port must be of mode in, buffer or linkage. It may not be a port of mode out. Thus, the D_flipflop component in Figure 21-1 would have to be a buffer output port. Any flipflop entity bound to the flipflop instances in the counter would also require a buffer output port. Third, a buffer port can only have one source. Hence we cannot resolve a number of sources to determine the value of a buffer port. Finally, while we can associate an actual signal with a buffer port of a component instance, that port must be the only source of the signal. Thus, we cannot use a buffer port of a component as one of a number of contributors to a resolved signal. These restrictions severely limit the uses of buffer ports, so they are not commonly used in VHDL-87 or VHDL-93.

Linkage ports are provided as a means of connecting signals to foreign design entities. If the implementation of an entity is expressed in some language other than VHDL, the way in which values are generated and read within the entity may not conform to the same transaction semantics as those of VHDL. A linkage mode port provides the point of contact between the non-VHDL and the VHDL domains. Unless a simulator provides some additional semantics for generating and reading linkage ports, a model containing linkage ports anywhere in the hierarchy cannot be simulated.

Since the internal operation of a linkage port is not bound by the rules of VHDL, VHDL takes the safe approach and considers a linkage port connected to a signal to be both a reader and a source for the signal. Thus, if the linkage port is connected to an actual signal that is a port of an enclosing entity, the actual port cannot be of mode in or out. It must be a port of mode inout, buffer or linkage. One further restriction is that linkage ports may not have default expressions in their declarations.

One final point to note about linkage ports is that they are deprecated in the current version of the VHDL Language Reference Manual and may be removed from the language in subsequent versions. Thus we should avoid using them in new designs.

21.2 Conversion Functions in Association Lists

In the preceding chapters, we have seen uses of association lists in generic maps, port maps and subprogram calls. An association list associates actual values and objects with formal objects. Let us now look at the full capabilities provided in association lists, shown by the following full syntax rules:

The simple rules for association lists we used previously allowed us to write associations of the form “formal => actual". These new rules allow us to write associations such as

These associations include conversion functions or type conversions. We discussed type conversions in Chapter 2. They allow us to convert a value from one type to another closely related type. A conversion function, on the other hand, is an explicitly or implicitly declared subprogram or operation. It can be any function with one parameter and can compute its result in any way we choose.

A conversion in the actual part of an association is invoked whenever a value is passed from the actual object to the formal object. For a variable-class subprogram parameter, conversion occurs when the subprogram is called. For a signal associated with a port, conversion occurs whenever an updated signal value is passed to the port. For constant-class subprogram parameters and for generic constants, the actual values are expressions, which may directly take the form of function calls or type conversions. In these cases, the conversion is not considered to be part of the association list; instead, it is part of the expression. Conversions are not allowed in the remaining cases, namely, signal-class and file-class actual subprogram parameters.

EXAMPLE    

We wish to implement a limit checker, which checks whether a signed integer is out of specified bounds. The integer and bounds are represented as standard-logic vectors of the subtype word, declared in the package project_util as

We can use a comparison function that compares integers represented as bit vectors. The function is declared in project_util as

The entity declaration and architecure body for the limit checker are shown in Figure 21-2. The process performs the comparisons by converting the word values to bit vectors, using the conversion function word_to_bitvector. Note that we cannot use the function To_bitvector itself in the actual part of the association list, as it has two parameters, not one. Note also that the result type of the conversion function in this example must be a constrained array type in order to specify the array index range for the actual value passed to the comparison function.

FIGURE 21-2 An entity and architecture body fora flipflop.

A conversion can only be included in the actual part of an association if the interface object is of mode in, inout or linkage. If the conversion takes the form of a type conversion, it must name a subtype that has the same base type as the formal object and is closely related to the type of the actual object. If the conversion takes the form of a conversion function, the function must have only one parameter of the same type as the actual object and must return a result of the same type as the formal object. If the interface object is of an unconstrained array type, the type mark of the type conversion or the result type of the conversion function must be constrained. The index range of the type mark or function result is used as the index range of the interface object.

A conversion in the formal part of an association is invoked whenever a value is passed from the formal object to the actual object. For a variable-class procedure parameter, conversion occurs when the procedure returns. For a signal associated with a port, conversion occurs whenever the port drives a new value. Conversions are not allowed for signal-class and file-class formal subprogram parameters.

EXAMPLE    

Suppose a library contains the following entity, which generates a random number at regular intervals:

If we have a test bench including signals of type bit, we can use the entity to generate random stimuli. We use a conversion function to convert the numbers to bit-vector values. An outline of the test bench is shown in Figure 21-3. The function natural_to_bv11 has a parameter that is a natural number and returns a bit-vector result. The architecture instantiates the random_source component, using the conversion function in the formal part of the association between the port and the signal. Each time the component instance generates a new random number, the function is invoked to convert it to a bit vector for assignment to stimulus_vector.

FIGURE 21-3 An outline of a test-bench architecture body, including a random stimulus generator.

The type requirements for conversions included in the formal parts of associations mirror those of conversions in actual parts. A conversion can only be included in a formal part if the interface object is of mode out, inout, buffer or linkage. If the conversion takes the form of a type conversion, it must name a subtype that has the same base type as the actual object and is closely related to the type of the formal object. If the conversion takes the form of a conversion function, the function must have only one parameter of the same type as the formal object and must return a result of the same type as the actual object. If the interface object is of an unconstrained array type, the type mark of the type conversion or the parameter type of the conversion function must be constrained. The index range of the type mark or function parameter is used as the index range of the interface object.

Note that we can include a conversion in both the formal part and the actual part of an association if the interface object is of mode inout or linkage. The conversion on the actual side is invoked whenever a value is passed from the actual to the formal, and the conversion on the formal side is invoked whenever a value is passed from the formal to the actual.

One important use of type conversions in association lists arises when we mix arrays of unresolved and resolved elements in a model. For example, the standard-logic package declares the two types:

These are two distinct types, even though the element type of std_logic_vector is a subtype of the element type of std_ulogic_vector. Thus, we cannot directly associate a std_ulogic_vector signal with a std_logic_vector port, nor a std_logic_vector signal with a std_ulogic_vector port. However, we can use type conversions or conversion functions to deal with the type mismatch.

EXAMPLE    

Suppose we are developing a register-transfer-level model of a computer system. The architecture body for the processor is shown in Figure 21-4. We declare a latch component and a ROM component, both with unresolved ports. We also declare a constrained array subtype std_logic_word with resolved elements and a number of signals of this subtype representing the internal buses of the processor.

We instantiate the latch component and associate the destination bus with the d port and the sourcel bus with the q port. Since the signals and the ports are of different but closely related types, we use type conversions in the association list. Although the types std_uloglc_vector and std_logic_vector are unconstrained array types, we can name them in the type conversion in this instance, since the component ports are constrained.

FIGURE 21-4An outline of a computer system model using type conversions to associate array signals with array ports.

We also instantiate the ROM component and associate the source2 bus with the d_out port. Here also we use a type conversion in the association list. However, the port d_out is of an unconstrained type. Hence we may not use the name std_logic_vector in the type conversion, since it, too, is unconstrained. Instead, we use the constrained subtype name std_logic_word. The index range of this subtype is used as the index range of the port d_out in the component instance.

VHDL-87

VHDL-87 does not allow type conversions in association lists, but does allow conversion functions. If we need to convert between closely related types in an association list, we can write a function that performs the type conversion and use the function as a conversion function in the association list.

21.3 Postponed Processes

VHDL provides a facility, postponed processes, that is useful in delta-delay models. A process is made postponed by including the keyword postponed, as shown by the full syntax rule for a process:

The difference between a postponed process and a normal process lies in the way in which they are resumed during simulation. In our discussion of the simulation cycle in Chapter 5, we said that a normal process is triggered during a simulation cycle in which one of the signals to which it is sensitive changes value. The process then executes during that same simulation cycle. A postponed process is triggered in the same way, but may not execute in the same cycle. Instead, it waits until the last delta cycle at the current simulation time and executes after all non-postponed processes have suspended. It must wait until the non-postponed processes have suspended in order to ensure that there are no further delta cycles at the current simulation time. In addition, during initialization, a postponed process is started after all normal processes have been started and have suspended.

When we are writing models that use delta delays, we can use postponed processes to describe “steady state" behavior at each simulation time. The normal processes are executed over a series of delta delays, during which signal values are determined incrementally. Then, when all of the signals have settled to their final state at the current simulation time, the postponed processes execute, using these signal values as their input.

EXAMPLE    

We can write an entity interface for a set-reset flipflop as shown in Figure 21-5. The entity declaration includes a process that verifies the outputs of the flipflop. Every implementation of the interface is required to produce complementary outputs. (The condition “now = 0 fs“ is included to avoid an assertion violation during initialization.)

Figure 21-5 also shows a dataflow architecture of the flipflop. The concurrent signal assignment statements gate_1 and gate_2 model an implementation composed of cross-coupled gates. Assume that the flipflop is initally in the reset state. When s_n changes from ‘1’ to ‘0’, gate_1 is resumed and schedules a change on q from ‘0’ to ‘1’ after a delta delay. In the next simulation cycle, the change on q causes gate_2 to resume. It schedules a change on q_n from ‘1’ to ‘0’ after a delta delay. During the first delta cycle, q has the new value ‘1’, but q_n still has its initial value of ‘1’. If we had made the verification process in the entity declaration a non-postponed process, it would be resumed in the first delta cycle and report an assertion violation. Since it is a postponed process, it is not resumed until the second delta cycle (the last delta cycle after the change on s_n), by which time q and q_n have stabilized.

FIGURE 21-5 An entity declaration and architecture body for a set-reset flipflop.

It is important to note that the condition that triggers a postponed process may not obtain when the process is finally executed. For example, suppose a signal s is updated to the value ‘1’, causing the following postponed process to be triggered:

Because the process is postponed, it is not executed immediately. Instead, some other process may execute, assigning ‘0’ to s with delta delay. This assignment causes a delta cycle during which s is updated to ‘0’. When p is eventually executed, it proceeds with the statements immediately after the wait statement. However, despite the appearance of the condition in the wait statement, s does not have the value ‘1’ at that point.

Since each postponed process waits until the last delta cycle at a given simulation time before executing, there may be several postponed processes triggered by different conditions in different delta cycles, all waiting to execute. Since the cycle in which the postponed processes execute must be the last delta cycle at the current simulation time, the postponed processes must not schedule transactions on signals with delta delay. If they did, they would cause another delta cycle at the current simulation time, meaning that the postponed processes should not have executed. The restriction is required to avoid this paradox.

In previous chapters, we described a number of concurrent statements that are equivalent to similar sequential statements encapsulated in processes. We can write postponed versions of each of these by including the keyword postponed at the beginning of the statement, as shown by the following syntax rules:

Inclusion of the keyword postponed simply makes the encapsulating process a postponed process. Thus, we can rewrite the postponed process in the example on page 624 as

VHDL-87

Postponed processes are not provided in VHDL-87.

21.4 Shared Variables

When we introduced variables in Chapter 2, we noted that they can only be declared in processes; hence only one process can access each variable. We have also seen variables declared in subprograms, in which case they are local to the invocation of the subprogram. The reason for these restrictions is to prevent indeterminate results arising from a number of processes accessing a variable in an indeterminate order during a simulation cycle. In some circumstances, however, it is desirable to allow a number of processes to share access to a variable. Either the fact of non-determinacy may be irrelevant, or the use of shared variables may allow a more concise and understandable model. VHDL provides a mechanism for sharing variables, shown by the full syntax rule for a variable declaration:

If we include the keyword shared in a variable declaration, the variables defined are called shared variables and can be accessed by more than one process. We can only declare shared variables in the places in a model where we cannot declare normal variables, namely, in entity declarations and architecture bodies, in block and generate statements and in packages. Unlike normal variables, there are a number of restrictions on the way we declare and use shared variables. We discuss these restrictions in this section.

Problems with shared variables potentially arise when the processes in the model are executed on a parallel computer, such as a multiprocessor workstation or a parallel supercomputer. They can also occur on a single-processor computer if the simulation kernel preemptively switches between processes. The simplest problem is that two processes trying to update a shared variable might interfere with each other, resulting in an unpredictable final result for the variable. Suppose, for example, that two processes try to increment a counter. Each process executes the statement

This statement typically involves reading the variable’s value, adding one to the value, then storing the result back into the variable’s memory location. If one process completes this sequence before the other starts, the variable is incremented by two, as expected. However, if both processes read the initial value before either performs the write, the variable is only incremented by one. Since the model writer has no control over the interleaving of memory access from multiple processes, the result is non-deterministic. More complex problems arise when the shared variable is not simply a scalar, but is instead a composite or dynamic data structure requiring complex update operations. In these cases, interference between processes can put the data structure in an inconsistent state, resulting in lost or corrupted data.

The key to avoiding interference between processes that concurrently access a shared variable is mutual exclusion. A process must acquire exclusive access when it needs to read or update the variable using some sequence of instructions. While the process is performing those instructions, no other process is allowed access to the variable. This rule is enforced by the language implementation.

We achieve mutual exclusion for a shared variable by declaring the variable to be of a protected type. There are two parts to the definition of a protected type: a protected type declaration and a protected type body, each of which is included in a type declaration. The extended syntax rule for type declarations is included in Appendix E. The protected type declaration specifies the interface of the protected type. It contains methods, subprograms that will be used by processes to access the shared variable with mutual exclusion. The syntax rule for a protected type declaration is

The optional identifier in the syntax rule indicates that the name of the protected type may be repeated at the end of the declaration. The declarations in the declarative part can include subprogram (procedure and function) declarations for the methods of the type, attribute specifications and use clauses. Only the interfaces of subprograms are included; the subprogram bodies are deferred to the protected type body.

A simple example of a protected type declaration is

This declares a protected type for a shared counter with methods to reset the counter value to zero, to increment the counter by some amount and to read the value of the counter.

We can declare a shared variable to be of this type using a shared variable declaration, for example:

A process uses the name of the shared variable as a prefix to a method name to identify the shared variable on which the method is invoked. For example:

In each case, the process acquires exclusive access to event_counter before executing the body of the method. While the process is executing the method, other processes that try to invoke any method on the same shared variable must wait. When the first process finishes executing its method, it releases exclusive access to the shared variable. One of the waiting processes may then resume. The order in which waiting processes are chosen for resumption is not defined.

A protected type body specifies the implementation details of a protected type. The syntax rule for a protected type body is

Again, the optional identifier in the syntax rule indicates that the name of the protected type may be repeated at the end of the declaration. The declarations in the declarative part can include subprogram declarations and bodies; type, subtype, constant, variable, file and alias declarations; attribute declarations and specifications; group templates and group declarations; and use clauses. We must include subprogram bodies for the methods declared in the protected type declaration. Items declared within a protected type body are not visible outside the protected type, so the only way a process can access the items is by using the methods of the protected type.

Note that we usually include variable declarations in a protected type body. These variables constitute the data stored in a shared variable of the protected type. We might also include file declarations. In this case, the methods of the protected type can be used to ensure that multiple lines of output to a file from a process are performed atomically, rather than being interleaved with output from other processes. We would not normally write a protected type body without variable or file declarations, since there would be no need for mutual exclusion in that case.

As an example, we can implement the shared counter protected type as follows:

The variable count represents the storage for the counter type. Each shared variable of this type has its own instance of the count variable. The methods simply operate on the variable to update or read its variable without interference from other processes.

There are a number of rules governing the use of protected types. In summary, the rules are the following:

• Only method names declared in the protected type declaration are visible outside the protected type definition. Nothing declared in the protected type body is visible outside. However, all names declared in the protected type declaration are visible in the corresponding protected type body. This rule is analogous to the visibility rule for names declared in package declarations and package bodies.
• If a protected type is declared in a package declaration, the protected type body must be declared in the corresponding package body. This is similar to the way in which subprogram specifications and bodies are declared in packages. In other cases, the protected type body must be declared in the same declarative region as the protected type declaration.
• Only variables and variable-class subprogram parameters can be of protected types. Actual values of protected type subprogram parameters are passed by reference. This ensures that when the subprogram invokes a method of the parameter, exclusive access is acquired to the actual shared variable, not to a copy of the variable.
• Shared variables must be of protected types. Other variables may be of protected types, but since they are only accessible to the process in which they are declared, there is no need for mutual exclusion.
• Protected types cannot be used as elements of files, as elements of composite types, nor as types designated by access types.
• Variable assignment of one protected-type variable to another is not allowed, since assignment does not provide mutual exclusion for the operands. As a consequence, a protected-type variable must not have an initial value expression in its declaration.
• Similarly, the equality (“=") and inequality (“/=") operators are not predefined for protected types, since they do not provide mutual exclusion.
• A protected type method must not include or execute a wait statement. This ensures that all methods complete and release exclusion within one simulation cycle. Processes that must share information across different simulation cycles should use signals for communication.
• A function method must be declared as impure, since it accesses the variables declared within the protected-type body that are outside the declaration of the function itself.
• A function method that is a unary operator must be declared with no parameters, since the variable on which is is invoked is implicitly the parameter. Similarly, a function method that is a binary operator must be declared with only one parameter. Such function methods can only be invoked using the selected-name notation; they cannot be invoked using infix operator notation.

One scenario in which we might use shared variables is to describe passive shared objects that are accessed concurrently by different parts of a design. An example of such an object is a register file in a pipelined CPU, represented simply as an array of bit vectors. The register file must be accessed by the processes representing the operand fetch stage and the result write-back stage. At a lower level of abstraction, we would implement the register file as a component with read and write ports. Read and write operations would take place over signals and would conform to some signaling protocol. However, at the behavioral level of abstraction, we can describe the register file more simply as a shared variable of a protected type with read and write methods. The mutual exclusion afforded by the protected type ensures that reads and writes do not interfere with each other.

EXAMPLE    

Figure 21-6 shows an entity declaration and architecture body for a two-port register that is read using one port and written using the other. The protected type protected_reg encapsulates the register data and provides get and set methods to access the storage. The shared variable reg_store represents the storage of the register. The two processes reader and writer implement the behavior for read-port and write-port accesses, respectively.

FIGURE 21-6 An entity and architecture body for a two-port register.

Another scenario in which we might use shared variables is to instrument a model. We can use a shared variable to collect information about the behavior of processes within a design over the course of a simulation run. Each process updates the variable using update methods as events of interest occur. Mutual exclusion ensures that concurrent updates do not conflict.

EXAMPLE    

In this example we use shared variables to instrument a behavioral model of a multiprocessor computer system, shown in Figure 21-7. Each processing element (PE) has an attached level 2 (L2) cache. The shared variable cache counters is used to collect sharing statistics for blocks in the address space. Processes representing caches invoke methods to record accesses to memory blocks. The log controller process periodically writes the recorded statistics to a log file.

The package declaration cache_instrumentation, shown in Figure 21-8, includes a protected-type declaration for the instrumentation data structure. The protected type includes methods for recording read and write misses and for writing the recorded data to a file. The package also declares a shared variable of the protected type for collecting the data. The implementations of the data structure and the methods are described in the protected-type body, which is declared in the package body shown in Figure 21-8. The protected-type body includes an array of records, one for each block of the multiprocessor’s memory space. Each record contains counters for the different kinds of misses to be logged.

The individual caches are described by a behavioral architecture body, shown in Figure 21-9. The cache controller process invokes the instrumentation methods when cache miss events occur.

FIGURE 21-7 An instrumented multiprocessor computer system with caches.

FIGURE 21-8 A package declaration and body for the cache instrumentation.

FIGURE 21-9 An outline of the behavioral architecture bodyfor the cache, showing invocation of the instrumentation methods.

The complete multiprocessor system is described by an architecture body, shown in Figure 21-10. The generate statement creates multiple instances of a processing element, each with an attached cache. Since each cache instance includes a cache controller process, there are multiple cache controller processes that may concurrently access the instrumentation variable in the instrumentation package. Furthermore, the multiprocessor model includes the log_controller process that periodically invokes the dump_log method. All of these processes share access to the instrumentation variable, so mutual exclusion is required to prevent interference.

FIGURE 21-10 An outline of the architecture body describing the complete instrumented multiprocessor system.

An important point to note in using VHDL protected types is the potential for deadlock. It is possible to write a model in which two processes block waiting for mutual exclusion over shared variables and can never resume. To illustrate the possibility, consider an extension of the shared counter example. Suppose we augment the protected type to include a method to copy the value from one counter to another. We declare the method in the protected type declaration as

We declare the method implementation in the protected-type body as

We declare two shared variables as

Now consider what might happen if process P1 executes the statement

and process P2 executes

A possible interleaving of execution involves P1 acquiring access to a and P2 acquiring access to b, before either reaches the body of the method. Note that passing a variable of protected type simply involves passing a reference; it does not involve acquiring access to the variable. When P1 reaches the invocation of the value method within the copy method, it tries to acquire access to b. Since P2 already has access to b, Pi blocks. Similarly, when P2 reaches the invocation of the value method, it tries to acquire access to a. Pl already has access to a, so P2 blocks. Neither process can proceed, and execution deadlocks.

While this is a contrived example, it illustrates one situation under which deadlock can arise. VHDL does not prohibit such situations, nor does it require that a simulator detect or resolve deadlock. When writing models using protected types, we must take care not to introduce the potential for deadlock. One way is to ensure that all processes acquire exclusive access to collections of variables in the same order. Of course, if processes only need exclusive access to one variable at a time, the protected-type mechanism does not deadlock.

VHDL-87

Shared variables are not provided in VHDL-87.

VHDL-93

In VHDL-93, shared variables need not be of protected types. Instead, they are like normal unshared variables. A shared variable can be declared of any type except a file type, and the declaration can include an initial value expression. The value of a shared variable can be used in an expression and can be updated using a normal variable assignment statement. The difference is that there is no mutual exclusion for access to a shared variable by different processes in a given simulation cycle. This means that read and assignment operations by different processes might interfere with one another. Hence we must take great care when using shared variables in VHDL-93 to ensure that only one process can access each shared variable in each simulation cycle. The VHDL-93 language specification deems a model to be in error if it depends on the value of a shared variable accessed by more than one process during any simulation cycle.

Exercises

1. [ 21.2] Suppose we wish to associate an out mode port of type std_logic with a signal of type bit. Why can we not use the function To_bit as a conversion function in the association?

2. [ 21.2] Suppose we have a gate component declared as

Write a component instantiation statement that instantiates the gate, with inputs connected to signals s1 and s2 and output connected to the signal s3. All of the signals are of type bit. Use conversion functions where required.

3. [ 21.4] Suppose, using VHDL-93, we have a shared variable declared as follows in a package instrumentation:

We have two instances, ml and m2, of a behavioral multiplier model that includes the statement

Show how, in VHDL-93, the variable may be updated incorrectly if we allow the two instances to access the variable in the same simulation cycle.

4. [ 21.4] Write a protected type declaration and body that provides mutual exclusion for an integer shared variable. The protected type should include a method called set to update the variable value and a method called get to retrieve the variable value.

5. [ 21.1] Develop a structural model of an SR-flipflop constructed from nor gates as shown in Figure 21-11. Use buffer mode ports for q and q_n.

FIGURE 21-11 An SR-flipflop constructed from not gates.

6. [ 21.2] Develop a behavioral model of a counter that counts from 0 to 255 with an output port of type natural. In a test bench, define and instantiate an eight-bit counter component. Write a configuration declaration for the test bench, binding the behavioral counter entity to the counter component instance. Use conversion functions in the binding indication as required. You may wish to use the conversion functions from the bv_arithmetic package described in Chapter 10. Note that a configuration declaration can use items, such as conversion functions, declared in separate packages.

7. [ 21.3] Exercise 17 in Chapter 11 describes a distributed priority arbiter for a shared-bus multiprocessor system. Each requester computes the minimum of all priorities. Develop a model of the minimization circuit that operates using delta delays. Include a number of instances of the minimizer in a test bench. Also include a process that verifies that the result priority is the minimum of all of the request priorities when the computation is complete.

8. [ 21.4] The bounded buffer ADT in Section 17.3 could be used to send a stream of bytes from one process to another. However, if the sender called the write procedure at the same time as the receiver called the read procedure, they may interfere and cause corruption of the state of the buffer. Develop a package that defines a protected type for a protected bounded buffer. The protected-type body should encapsulate a variable of the bounded buffer ADT from Section 17.3. The protected type should include methods to test whether the buffer is full, whether it is empty, and to read and write bytes. Test your package in an architecture body with producer and consumer processes that communicate using a shared variable of the protected bounded buffer type.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.194.57