Q4VHDL
Question
Q.2. (a) Write VHDL code for a 4-bit down counter using sequential statements. [8]
(b) Define elaboration, signal driver using an example. [8]
Answer
(a) 4-bit Down Counter Using Sequential Statements
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity down_counter4 is
port (
clk, reset : in STD_LOGIC;
count : out STD_LOGIC_VECTOR(3 downto 0)
);
end down_counter4;
architecture behavioral of down_counter4 is
signal count_int : STD_LOGIC_VECTOR(3 downto 0);
begin
process(clk, reset)
begin
if reset = '1' then
count_int <= "1111";
elsif rising_edge(clk) then
count_int <= count_int - 1;
end if;
end process;
count <= count_int;
end behavioral;This 4-bit down counter is written using sequential statements inside a process block, the standard technique for describing clocked (sequential) circuit behavior in VHDL. The process is sensitive to both clk and reset, giving it an asynchronous reset: whenever reset goes to '1', the counter is immediately forced to its maximum value "1111" (15 in decimal) regardless of the clock, modeling a real asynchronous reset input on a hardware flip-flop. On each rising edge of the clock (checked using the 'rising_edge' function, the standard, synthesizable way to detect a clock edge in VHDL), the counter decrements by one (count_int - 1, using the arithmetic operator provided by the STD_LOGIC_UNSIGNED package), counting down 15, 14, 13, ..., 0, and then wrapping around back to 15 due to the natural unsigned binary underflow behavior of the subtraction operation on a 4-bit vector. The internal signal count_int is used inside the process (since directly reading an 'out' mode port signal is not permitted in older VHDL versions) and is continuously assigned to the actual output port count outside the process.
(b) Elaboration and Signal Driver
Elaboration is the phase of VHDL processing (occurring after the source code has been compiled/analyzed but before simulation actually begins) during which the simulator builds the complete, concrete hierarchical structure of the design - instantiating every component, connecting all port maps, creating all signals with their initial values, and establishing the complete network of processes and their sensitivity lists - effectively converting the abstract, generic VHDL source-code description into a fully concrete, ready-to-simulate model of the specific design hierarchy being simulated. Elaboration must complete successfully before the actual event-driven simulation (described in relation to the previous question) can begin executing, since the simulator needs the fully elaborated design structure to know which processes exist, which signals they read and write, and how all the design's entities are interconnected.
A signal driver refers to any single concurrent statement (a process, a concurrent signal assignment, or a component instantiation port connection) that assigns a value to a given signal - a signal can have multiple drivers if more than one concurrent statement assigns to it, and in that case VHDL uses a resolution function (associated with the signal's type, such as std_logic's built-in resolution function) to determine the final resolved value of the signal by combining the values from all its active drivers according to defined resolution rules (for example, std_logic's resolution function treats a conflict between a driven '0' and a driven '1' as producing an unknown 'X' value, correctly modeling the physical reality of two hardware outputs fighting over the same wire). For example, in a bus structure where multiple tri-state driver components can each drive the same shared bus signal (but only one is active, driving 'Z' from the others), each component instantiation represents one driver of that shared signal, and understanding driver resolution is essential for correctly modeling and debugging any VHDL design containing shared, multiply-driven signals such as buses, tri-state outputs, or wired-OR/wired-AND structures.
It is worth contrasting this asynchronous-reset down counter with an alternative synchronous-reset formulation, since the choice between the two has real hardware implications. In the asynchronous-reset version shown here, the process sensitivity list includes both clk and reset, and the reset condition is checked first and independent of any clock edge, meaning the counter is forced to "1111" the instant reset is asserted, regardless of the clock - this maps directly to a flip-flop's dedicated asynchronous set/reset input, is fast-acting, but can, if the reset signal is not itself synchronized to the clock domain, risk being released (de-asserted) asynchronously close to a clock edge, creating a subtle reset-recovery timing hazard. A synchronous-reset alternative would instead check 'if rising_edge(clk) then if reset = '1' then ... ' entirely inside the clocked branch, guaranteeing the reset action itself only ever occurs synchronously with the clock, at the cost of the reset having no effect at all if the clock is not toggling.
The underflow wraparound behavior of this counter (0 rolling back to 15) is a direct and predictable consequence of using unsigned binary subtraction on a fixed-width vector: since "0000" - 1 in unsigned arithmetic modulo 16 produces "1111", the counter naturally cycles through all 16 states repeatedly without needing any explicit wraparound-detection logic, exactly analogous to how an up-counter's overflow from 15 back to 0 arises automatically from unsigned addition's natural modulo-16 wraparound. This makes free-running down counters (and up counters) particularly simple to describe in VHDL, since the modular arithmetic behavior that a designer wants is exactly what the underlying binary subtraction (or addition) operation already provides without any special-case handling, in contrast to a decade (0-9) or other non-power-of-two counter, which would require explicit comparison-and-reset logic to force the wraparound at the desired non-power-of-two boundary.
The distinction between this down counter's use of a signal (count_int) rather than a variable is also worth relating back to the general signal-versus-variable discussion elsewhere in this examination: because count_int must be readable outside the process (via the concurrent assignment 'count <= count_int'), it is necessarily declared as a signal rather than a variable, since variables are only visible within the process (or subprogram) in which they are declared - had the counter's internal state instead only been needed for computation entirely local to this one process, a variable could have been used instead, though the resulting synthesized hardware (a register holding the counter value) would be functionally identical either way, illustrating that the signal/variable choice here is driven by visibility requirements rather than by any difference in the underlying hardware being described.
Finally, note that both sub-parts of this question use an internal signal (count_int in part (a)) or reference the output port pattern consistently with idiomatic VHDL practice for driving 'out' mode ports, since directly reading back an entity's own output port inside a process is disallowed in VHDL-87/93 (though permitted in VHDL-2008); using an internal signal and a separate concurrent assignment to the port sidesteps this restriction and remains portable across VHDL tool versions.
Both sub-parts of this question also use meaningful, descriptive signal and port names (count_int, serial_in, parallel_out) rather than terse single-letter identifiers, a coding convention that materially improves readability in larger designs and is especially valuable when a design is later revisited, reused, or handed off to another engineer, since VHDL's strong typing and verbose syntax already provide considerable self-documentation, but well-chosen names further reduce the effort needed to understand a design's intent without needing to consult separate external documentation.