Status: Under active development
This project implements a digital "die roller" that generates a pseudorandom number (1–6) on a rising clock edge when roll is asserted, and displays the result as a word ("one" through "six") across seven-segment displays.
Once a value is determined, the device translates this numeric data into a word format displayed via a seven-segment display. Because standard seven-segment displays are limited in their character set, the design employs a "visual cheating" technique. For instance, the letter ‘w’ in "two" is approximated using two adjacent display characters, and the ‘x’ in "six" is rendered across three characters. These complex segment encodings are retrieved from a behavioural memory model via an Advanced Peripheral Bus (APB) read transaction. The final output is driven to the segments port, with a valid signal asserting once the data is stable.
This project was a structured hierarchical design exercise in Doulos's "Essential Digital Design Techniques" course, where the top-level of the design integrates different subcomponents as seen in Figure 1. The top-level design module, finite state machine, testbench, and behavioural memory model were provided.
My specific contributions to the design include:
- Synchronous Modulo-6 Counter(
Counter): Designed the Register Transfer Level (RTL) for the synchronous modulo-6 counter, which is instantiated and integrated as bothrandom_counterandaddress_counterin the top-level design module. - Synchronizer: Designed a two-flop synchronizer to reduce the probability that metastability from the asynchronous
rollinput propagates into the FSM. - APB Manager-Side Read Control and Memory Integration: Implemented the setup, access and idle control logic for zero-wait-state APB reads and integrated it with the course provided behavioural memory subordinate and segment-output path.
Note: RTL designs not authored by me are excluded from this repository for copyright compliance.
Figure 1: Project Architecture Block Diagram (Source: Essential Digital Design Techniques, Doulos).
- Functions as a 7-segment font lookup table which stores the mapped word encodings of a rolled die value.
- Utilizes the APB protocol for its read transactions.
- Stores word encodings in 8-byte blocks of 7-segment visual display patterns.
The synchronous modulo-6 counter (Counter) increments on the rising edge of clk and wraps back around to its initial value.
- Instance 1 (Pseudorandom Generator /
random_counter):- Continuously cycles through values to simulate a rolling die.
- Freezes on its current count when a roll is initiated (
random_value_enable = 0). - This frozen state becomes
random_value, serving as the 3-bit memory block selector for the selected die value.
Figure 2a: random_counter waveform showing continuous incrementing and value freezing when disabled.
- Instance 2 (Offset Indexing /
address_counter):- Remains idle until the FSM initiates the display read sequence.
- Increments on clock edges (
address_counter_enable = 1) to step through characters of the selected die word. - This incrementing state becomes
address_offset, serving as the lower 3 bits (byte offset index) of the memory lookup.
Figure 2b: address_counter waveform demonstrating idle holding and sequential address stepping when enabled.
Inputs
- Global System Clock: The primary timing reference for the top-level design and subcomponents.
- Asynchronous Reset: Initializes the resettable sequential blocks, including the FSM, modulo-6 counter, and behavioural memory model, to their defined startup states.
- Data Input: The asynchronous
rollsignal is synchronized to the system clock on the rising edge ofclkbefore entering the FSM control logic.
Synchronizer : process(clk)
begin
if rising_edge(clk) then
Roll_Sync1 <= roll;
Roll_Sync2 <= Roll_Sync1;
end if;
end process;Outputs
-
Segments: Each element of
segmentscorresponds to a single seven-segment display element and displays the generated die roller value as an array of logic values. -
Valid: Asserted when display output is stable and valid.
The FSM uses registered state with separate next-state and output-decoding logic, keeping Moore outputs independent of direct input changes and supporting predictable synchronous behaviour.
graph TB
%% Define Block Shapes (Using standard text strings to fix the glitch)
IN([Inputs])
NEXT_LOGIC["Next-State Logic\nCombinational"]
REG["State Register\n(Clocked Flip-Flops)"]
STATE_DOT((( )))
OUT_LOGIC["Output Logic\nCombinational"]
OUT([Outputs])
%% Main Forward Path
IN --> NEXT_LOGIC
NEXT_LOGIC -->|next_state| REG
%% State line exits the register and hits the wire junction split
REG -->|state| STATE_DOT
%% Junction splits into the Output Logic and the Feedback Loop
STATE_DOT --> OUT_LOGIC
OUT_LOGIC --> OUT
%% The True Feedback Loop
STATE_DOT ==>|state feedback loop| NEXT_LOGIC
%% Direct Styling
style STATE_DOT fill:#333,stroke:#333
style IN fill:#eaeaea,stroke:#333,stroke-width:1px
style NEXT_LOGIC fill:#d4e6f1,stroke:#2980b9,stroke-width:2px
style REG fill:#d4e6f1,stroke:#2980b9,stroke-width:2px
style OUT_LOGIC fill:#d4e6f1,stroke:#2980b9,stroke-width:2px
style OUT fill:#eaeaea,stroke:#333,stroke-width:1px
Figure 3a: Synchronous Moore FSM Block Diagram
The operational behavior of the 5-state FSM is mapped out in the state diagram below.
stateDiagram-v2
direction TB
[*] --> initialization : Asynchronous reset
state "Initialization" as initialization
state "Start Reading" as start_reading
state "Continue Reading" as continue_reading
state "Save Data" as save_data
state "Output Segments" as output_segments
note right of initialization
Moore outputs:
random_value_enable = 1
end note
note right of start_reading
Moore outputs:
APB_1st_Cycle = 1
random_value_enable = 0
address_counter_enable = 0
end note
note right of continue_reading
Moore outputs:
APB_2nd_Cycle = 1
address_counter_enable = 1
random_value_enable = 0
end note
note right of save_data
Moore outputs:
APB_save_data = 1
random_value_enable = 0
end note
note right of output_segments
Moore outputs:
valid = 1
address_counter_enable = 1
random_value_enable = 1
end note
initialization --> start_reading : Roll_Sync2 = '1'
initialization --> initialization : Roll_Sync2 = '0'
start_reading --> continue_reading : Unconditional
continue_reading --> save_data : Unconditional
save_data --> start_reading : read_another = '1'
save_data --> output_segments : read_another = '0'
output_segments --> start_reading : Roll_Sync2 = '1'
output_segments --> initialization : Roll_Sync2 = '0'
Figure 3b: Five-State Moore FSM State Diagram
State Description Table
The table below details the exact behavioural purpose of each operational state and the corresponding transition requirements:
| State Name | System Activity (What the Hardware is Doing) | Transition Condition (How it leaves this state) |
|---|---|---|
initialization |
Enables random_value_enable = 1 to cycle the pseudorandom generator while holding APB setup lines idle and data flags invalid. |
Moves to start_reading if data input Roll_Sync2 goes high; otherwise, it remains in this idle loop. |
start_reading |
Triggers the 1st cycle of the APB read transaction control signal (APB_1st_Cycle) and freezes the random generator control signal (random_value_enable = 0). |
Automatically advances to continue_reading on the very next clock edge (Unconditional). |
continue_reading |
Asserts the second cycle of the APB read transaction control signal (APB_2nd_Cycle) and triggers the offset indexing control signal address_counter_enable = 1. |
Automatically advances to save_data on the very next clock edge (Unconditional). |
save_data |
Asserts the data save flag (APB_save_data) to store incoming data bus signals into internal registers. |
Loops back to start_reading if the read_another boundary check passes. Otherwise, it moves to output_segments. |
output_segments |
Asserts the data valid output flag, re-enables the random_counter, and continues driving the address_counter. |
Loops directly back to start_reading if data input (Roll_Sync2) is high. Otherwise, returns to initialization. |
Instance 1 (random_counter):
random_counter :
entity work.Counter(RTL)
port map (Reset => reset,
Clock => clk,
Enable => random_value_enable,
Q => random_value);Instance 2 (address_counter):
address_counter :
entity work.Counter(RTL)
port map (Reset => reset,
Clock => clk,
Enable => address_counter_enable,
Q => address_offset);Implemented the setup, access and idle control logic for zero-wait-state APB reads and integrated it with the course provided behavioural memory subordinate and segment-output path.
The top-level design uses glue logic to concatenate a 2-bit vector with random_value and address_offset to form the full target address (read_address) for memory lookups.
Behavioural Memory Model Integration
mem1 :
entity work.memory(behav_mem)
port map (reset => reset,
pclk => clk,
penable => penable,
psel => psel,
pwrite => pwrite,
paddr => paddr,
pwdata => pwdata,
prdata => prdata,
pready => pready );APB Manager 1st, 2nd, and idle cycles read transaction integrations
The manager executes read transactions using APB timing protocol:
- 1st Cycle (Setup Phase /
APB_1st_Cycle):pselis asserted whilepenableremains low, to signal transaction initiation. Simultaneously, subordinate selection (psel = '1'), read mode (pwrite = '0'), and address (paddr = read_address) are presented to the bus. - 2nd Cycle (Access Phase /
APB_2nd_Cycle):penableis asserted high ('1') while control signals (psel,paddr) remain valid. The memory subordinate drives character data ontoprdataduring this phase.pwdataremains inactive. PREADY& Wait States: Operates on a zero wait-state model wherepreadymust remain'1'. An internal assertion halts simulation (severity failure) ifpreadygoes low, ensuring fixed 2-cycle completion.- Idle State: During idle,
pselis low, so no APB transaction is active; this implementation leavespenablehigh until the next setup phase.
APB_Control :
process(APB_1st_Cycle, APB_2nd_Cycle, read_address)
begin
-- APB signals 1st cycle
if APB_1st_Cycle = '1' then
penable <= '0';
psel <= '1';
pwrite <= '0';
paddr <= read_address;
pwdata <= (others => '0');
-- APB signals 2nd cycle
elsif APB_2nd_Cycle = '1' then
penable <= '1';
psel <= '1';
pwrite <= '0';
paddr <= read_address;
pwdata <= (others => '0');
-- APB signals idle cycle
else
penable <= '1';
psel <= '0';
pwrite <= '0';
paddr <= (others => '0');
pwdata <= (others => '0');
end if;
end process APB_Control;The course provided testbench generates twenty roll transactions. I used its simulator output to verify the operation of my counter, synchronizer and APB integration within the complete system.
Figure 4: Word encodings of the random die roll value.
Digital-Design/
├── .gitignore # Simulator generated files and directories
├── README.md # Project description
└── VHDL/
└── die/ # Specific contribution module
├── Counter.vhd # Synchronous modulo-6 counter module
└── Run.do # Executes simulation workflow but depends on excluded course files
QuestaSim/ModelSim or another compatible VHDL simulator
VHDL-2008 support
Course provided source and testbench files for full system simulation
Clone the repository:
git clone https://github.com/DamiProject/Digital-Design.git
cd Digital-Design/VHDL/dieReproducibility Note: The complete design cannot be compiled directly from this repository because the course provided top-level module, FSM, memory model and testbench are excluded for copyright compliance. This repository contains only the RTL authored by me, along with selected integration excerpts and simulation evidence demonstrating its operation within the complete system.
Damilola Awotunde
MEng, Communications & Signal Processing - Western University | LinkedIn