Skip to content

Commit 2b4df9a

Browse files
committed
quest 34: Added HDL, tb & wave for mealy_finite_state_machine
1 parent b6b25aa commit 2b4df9a

3 files changed

Lines changed: 374 additions & 0 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
--! ----------------------------------------------------------------------------
2+
--! @author N. Selvarajah
3+
--! @brief Based on chipdev.io question 34
4+
--! @details VHDL module for Mealy Finite State Machine
5+
--! @details Ouputs depend on both the present state and the current inputs.
6+
--! ----------------------------------------------------------------------------
7+
8+
library ieee;
9+
use ieee.std_logic_1164.all;
10+
use ieee.numeric_std.all;
11+
12+
entity mealy_finite_state_machine is
13+
port (
14+
clk: in std_ulogic;
15+
rst_n: in std_ulogic;
16+
din: in std_ulogic;
17+
cen: in std_ulogic;
18+
doutx: out std_ulogic;
19+
douty: out std_ulogic
20+
);
21+
end entity;
22+
23+
architecture behavioural of mealy_finite_state_machine is
24+
type state_t is (S0, S1, S2, S3, S4);
25+
signal present_state, next_state: state_t;
26+
signal din_reg: std_ulogic;
27+
signal cen_reg: std_ulogic;
28+
begin
29+
state_register: process(clk, rst_n)
30+
begin
31+
if rst_n = '0' then
32+
present_state <= S0;
33+
din_reg <= '0';
34+
cen_reg <= '0';
35+
elsif rising_edge(clk) then
36+
present_state <= next_state;
37+
din_reg <= din;
38+
cen_reg <= cen;
39+
end if;
40+
end process;
41+
42+
next_state_logic: process(present_state, din_reg)
43+
begin
44+
next_state <= present_state;
45+
46+
case present_state is
47+
-- Initial state
48+
when S0 =>
49+
next_state <= S3 when din_reg else S1;
50+
-- Saw one 0 or two or more 0s (000, 010, 100, 110)
51+
when S1 | S2 =>
52+
next_state <= S3 when din_reg else S2;
53+
-- Saw one 1 or two or more 1s (111, 101, 011, 001)
54+
when S3 | S4 =>
55+
next_state <= S4 when din_reg else S1;
56+
when others =>
57+
next_state <= S0;
58+
end case;
59+
end process;
60+
61+
output_logic: process(present_state, din_reg, cen_reg)
62+
begin
63+
doutx <= '0';
64+
douty <= '0';
65+
66+
if ((present_state = S1 or present_state = S2) and din_reg = '0') or ((present_state = S3 or present_state = S4) and din_reg = '1') then
67+
doutx <= cen_reg;
68+
end if;
69+
70+
if (present_state = S2 and din_reg = '0') or (present_state = S4 and din_reg = '1') then
71+
douty <= cen_reg;
72+
end if;
73+
end process;
74+
end architecture;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
onerror {resume}
2+
quietly WaveActivateNextPane {} 0
3+
add wave -noupdate -divider DuT
4+
add wave -noupdate -divider Interface
5+
add wave -noupdate /tb_mealy_finite_state_machine/clk
6+
add wave -noupdate /tb_mealy_finite_state_machine/rst_n
7+
add wave -noupdate /tb_mealy_finite_state_machine/din
8+
add wave -noupdate /tb_mealy_finite_state_machine/cen
9+
add wave -noupdate /tb_mealy_finite_state_machine/doutx
10+
add wave -noupdate /tb_mealy_finite_state_machine/douty
11+
add wave -noupdate -divider Internal
12+
add wave -noupdate /tb_mealy_finite_state_machine/DuT/present_state
13+
add wave -noupdate /tb_mealy_finite_state_machine/DuT/next_state
14+
add wave -noupdate /tb_mealy_finite_state_machine/DuT/din_reg
15+
add wave -noupdate /tb_mealy_finite_state_machine/DuT/cen_reg
16+
add wave -noupdate -divider {tb - Internal}
17+
add wave -noupdate /tb_mealy_finite_state_machine/simulation_done
18+
TreeUpdate [SetDefaultTree]
19+
WaveRestoreCursors {{Cursor 1} {1975000 ps} 0}
20+
quietly wave cursor active 1
21+
configure wave -namecolwidth 150
22+
configure wave -valuecolwidth 100
23+
configure wave -justifyvalue left
24+
configure wave -signalnamewidth 1
25+
configure wave -snapdistance 10
26+
configure wave -datasetprefix 0
27+
configure wave -rowmargin 4
28+
configure wave -childrowmargin 2
29+
configure wave -gridoffset 0
30+
configure wave -gridperiod 1
31+
configure wave -griddelta 40
32+
configure wave -timeline 0
33+
configure wave -timelineunits ns
34+
update
35+
WaveRestoreZoom {0 ps} {10889550 ps}
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
--! ----------------------------------------------------------------------------
2+
--! @author N. Selvarajah
3+
--! @brief Based on chipdev.io question 34
4+
--! @details VHDL module for Mealy Finite State Machine
5+
--! ----------------------------------------------------------------------------
6+
7+
-- vunit: run_all_in_same_sim
8+
9+
library ieee;
10+
use ieee.std_logic_1164.all;
11+
use ieee.numeric_std.all;
12+
13+
library vunit_lib;
14+
context vunit_lib.vunit_context;
15+
16+
library osvvm;
17+
use osvvm.RandomPkg.RandomPType;
18+
use osvvm.RandomPkg.NaturalVSlType;
19+
use osvvm.CoveragePkg.all;
20+
21+
use work.tb_utils.all;
22+
23+
entity tb_mealy_finite_state_machine is
24+
generic (
25+
runner_cfg: string := runner_cfg_default
26+
);
27+
end entity;
28+
29+
architecture tb of tb_mealy_finite_state_machine is
30+
constant SIM_TIMEOUT: time := 1 ms;
31+
constant CLK_PERIOD: time := 10 ns;
32+
constant ENABLE_DEBUG_PRINT: boolean := false;
33+
34+
signal clk: std_ulogic := '0';
35+
signal rst_n: std_ulogic := '0';
36+
signal din: std_ulogic := '0';
37+
signal cen: std_ulogic := '0';
38+
signal doutx: std_ulogic;
39+
signal douty: std_ulogic;
40+
41+
signal simulation_done: boolean := false;
42+
begin
43+
generate_clock(clk => clk, FREQ => real(1 sec / CLK_PERIOD));
44+
45+
------------------------------------------------------------
46+
-- VUnit
47+
------------------------------------------------------------
48+
test_runner_watchdog(runner, SIM_TIMEOUT);
49+
50+
main: process
51+
begin
52+
test_runner_setup(runner, runner_cfg);
53+
info("Starting tb_mealy_finite_state_machine");
54+
55+
if ENABLE_DEBUG_PRINT then
56+
show(display_handler, debug);
57+
end if;
58+
59+
wait until simulation_done;
60+
log("Simulation done, all tests passed!");
61+
test_runner_cleanup(runner);
62+
wait;
63+
end process;
64+
65+
66+
checker: process
67+
constant PROPAGATION_TIME: time := 1 ns;
68+
variable random: RandomPType;
69+
variable coverage_model: CovPType;
70+
71+
procedure wait_clk_cycles(n: positive) is begin
72+
for i in 1 to n loop
73+
wait until rising_edge(clk);
74+
end loop;
75+
wait for PROPAGATION_TIME;
76+
end procedure;
77+
78+
procedure reset_module is begin
79+
rst_n <= '0';
80+
wait_clk_cycles(1);
81+
end procedure;
82+
83+
procedure start_module is begin
84+
rst_n <= '1';
85+
end procedure;
86+
87+
procedure test_example_1 is
88+
constant RST_N_SEQUENCE: std_ulogic_vector := (
89+
0 => '0', 1 to 7 => '1', 8 => '0', 9 to 13 => '1'
90+
);
91+
constant DIN_SEQUENCE: RST_N_SEQUENCE'subtype := (
92+
0 to 1 => '0', 2 to 8 => '1', 9 => '0', 10 => '1', others => '0'
93+
);
94+
constant CEN_SEQUENCE: RST_N_SEQUENCE'subtype := (
95+
9 | 12 to 13 => '0', others => '1'
96+
);
97+
constant EXPECTED_DOUTX_SEQUENCE: RST_N_SEQUENCE'subtype := "01011111000000";
98+
constant EXPECTED_DOUTY_SEQUENCE: RST_N_SEQUENCE'subtype := "00001111000000";
99+
begin
100+
info("1.0) test_example_1" & LF);
101+
102+
for i in RST_N_SEQUENCE'range loop
103+
rst_n <= RST_N_SEQUENCE(i);
104+
din <= DIN_SEQUENCE(i);
105+
cen <= CEN_SEQUENCE(i);
106+
wait_clk_cycles(1);
107+
check_equal(got => doutx, expected => EXPECTED_DOUTX_SEQUENCE(i), msg => "doutx at cycle " & to_string(i));
108+
check_equal(got => douty, expected => EXPECTED_DOUTY_SEQUENCE(i), msg => "douty at cycle " & to_string(i));
109+
end loop;
110+
111+
info("test_example_1 passed!" & LF);
112+
end procedure;
113+
114+
procedure test_zeros is
115+
constant REPETITIONS: positive := 10;
116+
begin
117+
info("2.0) test_zeros" & LF);
118+
reset_module;
119+
start_module;
120+
121+
cen <= '1';
122+
din <= '0';
123+
-- After reset, din_reg='0' already counts as first value
124+
-- So: i=1 -> 2 consecutive 0s (doutx='1'), i=2 -> 3 consecutive 0s (douty='1')
125+
for i in 1 to REPETITIONS loop
126+
wait_clk_cycles(1);
127+
check_equal(got => doutx, expected => '1', msg => "doutx at cycle " & to_string(i));
128+
check_equal(got => douty, expected => (i >= 2), msg => "douty at cycle " & to_string(i));
129+
end loop;
130+
131+
info("test_zeros passed!" & LF);
132+
end procedure;
133+
134+
procedure test_ones is
135+
constant REPETITIONS: positive := 10;
136+
begin
137+
info("3.0) test_ones" & LF);
138+
reset_module;
139+
start_module;
140+
141+
cen <= '1';
142+
din <= '1';
143+
for i in 1 to REPETITIONS loop
144+
wait_clk_cycles(1);
145+
check_equal(got => doutx, expected => (i >= 2), msg => "doutx at cycle " & to_string(i));
146+
check_equal(got => douty, expected => (i >= 3), msg => "douty at cycle " & to_string(i));
147+
end loop;
148+
149+
info("test_ones passed!" & LF);
150+
end procedure;
151+
152+
procedure test_random is
153+
constant CEN_WEIGHT: NaturalVSlType(std_ulogic'('0') to '1') := ('0' => 20, '1' => 80);
154+
constant TOTAL_ITERATIONS: positive := 1000;
155+
156+
variable din_history: std_ulogic_vector(2 downto 0) := (others => '0');
157+
variable history_count: natural := 0;
158+
variable expected_doutx, expected_douty: std_ulogic;
159+
begin
160+
info("4.0) test_random" & LF);
161+
162+
coverage_model.SetName("Random Test Coverage Model");
163+
164+
coverage_model.AddBins(Name => "rst_n", CovBin => GenBin(Min => 0, Max => 1, NumBin => 1));
165+
coverage_model.AddBins(Name => "cen", CovBin => GenBin(Min => 0, Max => 1, NumBin => 1));
166+
coverage_model.AddBins(Name => "din", CovBin => GenBin(Min => 0, Max => 1, NumBin => 1));
167+
coverage_model.AddBins(Name => "doutx", CovBin => GenBin(Min => 0, Max => 1, NumBin => 1));
168+
coverage_model.AddBins(Name => "douty", CovBin => GenBin(Min => 0, Max => 1, NumBin => 1));
169+
170+
rst_n <= '0';
171+
cen <= '0';
172+
din <= '0';
173+
din_history := (others => '0');
174+
history_count := 0;
175+
wait_clk_cycles(1);
176+
177+
for iteration in 1 to TOTAL_ITERATIONS loop
178+
rst_n <= random.DistSl(weight => RESET_N_WEIGHT);
179+
cen <= random.DistSl(weight => CEN_WEIGHT);
180+
din <= to_01(random.RandSl);
181+
182+
wait_clk_cycles(1);
183+
184+
-- Update coverage
185+
coverage_model.ICover(CovPoint => to_integer(unsigned'('0' & rst_n)));
186+
coverage_model.ICover(CovPoint => to_integer(unsigned'('0' & cen)));
187+
coverage_model.ICover(CovPoint => to_integer(unsigned'('0' & din)));
188+
189+
expected_doutx := '0';
190+
expected_douty := '0';
191+
192+
if rst_n = '0' then
193+
din_history := (others => '0');
194+
history_count := 0;
195+
else
196+
din_history := din_history(din_history'high - 1 downto din_history'low) & din;
197+
history_count := history_count + 1;
198+
199+
-- Check for 2 consecutive same values (doutx): reset value (din_reg='0') counts as first
200+
-- So history_count=1 means we have 2 values (reset + current)
201+
if history_count >= 1 then
202+
if din_history(1 downto 0) = "00" or din_history(1 downto 0) = "11" then
203+
expected_doutx := cen;
204+
end if;
205+
end if;
206+
207+
-- Check for 3 consecutive same values (douty): reset value counts as first
208+
-- So history_count=2 means we have 3 values (reset + 2 new inputs)
209+
if history_count >= 2 then
210+
if din_history = "000" or din_history = "111" then
211+
expected_douty := cen;
212+
end if;
213+
end if;
214+
end if;
215+
216+
coverage_model.ICover(CovPoint => to_integer(unsigned'('0' & doutx)));
217+
coverage_model.ICover(CovPoint => to_integer(unsigned'('0' & douty)));
218+
219+
check_equal(got => doutx, expected => expected_doutx, msg => "doutx at iteration " & to_string(iteration));
220+
check_equal(got => douty, expected => expected_douty, msg => "douty at iteration " & to_string(iteration));
221+
end loop;
222+
223+
info(
224+
"Coverage percentage: " & to_string(coverage_model.GetCov, "%.2f") & " %" &
225+
" (bins covered: " & to_string(coverage_model.GetTotalCovCount) & "/" & to_string(coverage_model.GetNumBins) & ")" & LF
226+
);
227+
info("Coverage details:" & LF);
228+
coverage_model.WriteBin;
229+
230+
info("test_random passed (" & to_string(TOTAL_ITERATIONS) & " iterations) !" & LF);
231+
end procedure;
232+
begin
233+
random.InitSeed(random'instance_name);
234+
235+
-- NOTE: Don't remove this, or else VUnit won't be able to run the tests
236+
wait_clk_cycles(1);
237+
238+
while test_suite loop
239+
if run("test_example_1") then
240+
test_example_1;
241+
elsif run("test_zeros") then
242+
test_zeros;
243+
elsif run("test_ones") then
244+
test_ones;
245+
elsif run("test_random") then
246+
test_random;
247+
else
248+
assert false report "No test has been run!" severity failure;
249+
end if;
250+
end loop;
251+
252+
simulation_done <= true;
253+
wait;
254+
end process;
255+
256+
DuT: entity work.mealy_finite_state_machine
257+
port map (
258+
clk => clk,
259+
rst_n => rst_n,
260+
din => din,
261+
cen => cen,
262+
doutx => doutx,
263+
douty => douty
264+
);
265+
end architecture;

0 commit comments

Comments
 (0)