1
Hi, I'm not quite sure if this vhdl code and testbench is correct for the given task. Can you take a look?
(kbin.social)
Hi, I'm not quite sure if this vhdl code and testbench is correct for the given task. Can you take a look?
Design a one-hour kitchen timer. The device should have buttons/switches to start and stop the timer, as well as to set the desired time interval for the alarm. Realize the task using the software package Quartus or in GHDL, confirm the correctness of the project task by simulation.
This is VHDL code:
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Kitchen_Timer is
port (
clk : in std_logic; -- Clock input
reset : in std_logic; -- Reset input
start : in std_logic; -- Start button input
stop : in std_logic; -- Stop button input
alarm : out std_logic -- Alarm output
);
end entity Kitchen_Timer;
-- Declare the architecture for the kitchen timer
architecture Behavioral of Kitchen_Timer is
signal count : integer range 0 to 3600 := 0; -- Counter for timer
signal alarming : std_logic := '0'; -- Signal to indicate alarming interval
signal alarm_en : std_logic := '0'; -- Signal to enable alarming interval
signal alarm_cnt : integer range 0 to 600 := 0; -- Counter for alarming interval
begin
-- Process to control the kitchen timer and alarming interval
process (clk, reset)
begin
if (reset = '1') then
count <= 0;
alarming <= '0';
alarm_en <= '0';
alarm_cnt <= 0;
elsif (rising_edge(clk)) then
if (stop = '1') then
count <= 0;
alarming <= '0';
alarm_en <= '0';
alarm_cnt <= 0;
elsif (start = '1' and count < 3600) then
count <= count + 1;
if (count = 3600) then
count <= 0;
alarming <= '0';
alarm_en <= '0';
alarm_cnt <= 0;
elsif (count > 0) then
alarm_en <= '1';
end if;
end if;
if (alarm_en = '1') then
if (alarm_cnt < 600) then
alarm_cnt <= alarm_cnt + 1;
else
alarm_cnt <= 0;
alarming <= '1';
end if;
end if;
end if;
end process;
-- Assign the alarm output
alarm <= alarming;
end architecture Behavioral; ```
This is Testbench:
```library ieee;
use ieee.std_logic_1164.all;
entity tb_Kitchen_Timer is
end tb_Kitchen_Timer;
architecture tb of tb_Kitchen_Timer is
component Kitchen_Timer
port (clk : in std_logic;
reset : in std_logic;
start : in std_logic;
stop : in std_logic;
alarm : out std_logic);
end component;
signal clk : std_logic;
signal reset : std_logic;
signal start : std_logic;
signal stop : std_logic;
signal alarm : std_logic;
constant TbPeriod : time := 1000 ns; -- EDIT Put right period here
signal TbClock : std_logic := '0';
signal TbSimEnded : std_logic := '0';
begin
dut : Kitchen_Timer
port map (clk => clk,
reset => reset,
start => start,
stop => stop,
alarm => alarm);
-- Clock generation
TbClock <= not TbClock after TbPeriod/2 when TbSimEnded /= '1' else '0';
-- EDIT: Check that clk is really your main clock signal
clk <= TbClock;
stimuli : process
begin
-- EDIT Adapt initialization as needed
start <= '0';
stop <= '0';
-- Reset generation
-- EDIT: Check that reset is really your reset signal
reset <= '1';
wait for 100 ns;
reset <= '0';
wait for 100 ns;
-- EDIT Add stimuli here
wait for 100 * TbPeriod;
-- Stop the clock and hence terminate the simulation
TbSimEnded <= '1';
wait;
end process;
end tb;
-- Configuration block below is required by some simulators. Usually no need to edit.
configuration cfg_tb_Kitchen_Timer of tb_Kitchen_Timer is
for tb
end for;
end cfg_tb_Kitchen_Timer;```
#science
@dejo I don't think this is going to work properly. If I understood correctly your alarm is going to start "alarming" (maybe ringing would be a better term here) after 10 minutes and reset an hour later. I also don't see a way to input or select the desired time for it to ring.
I would also change the stop signal to be the same as the reset (lookup alias) and remove the synchronous stop inside the main process. And the testbench is incomplete since it nevers starts nor its timer is set.
@T4V0 Thanks for the answer, but I think I didn't understand you very well, can you send me the code with the modifications so that I know what exactly you mean?
Thank you very much, in advance
@dejo
I would rather not, as it isn't a good learning experience for you, and would require some time for me to write the code.
Though if you have any questions about my previous answer, feel free to ask me about it.
As a freebie for you, pay attention to the
alarming
signal, and the condition that has been set: "The device should have buttons/switches to start and stop the timer, as well as to set the desired time interval for the alarm.". If I wanted the alarm to ring after 50 minutes, how would I do that? And what happens when the timer starts?From the code I see here, the alarm is going to ring 10 minutes after being started, and it won't stop until an hour passes. And it has no way to set a time for it to ring, it always rings after 10 minutes.
And, not only that, the
start
signal is never set in the testbench, so the timer is never going to begin.@T4V0 Hello, I've been doing a lot of research on this project these days and I've brought the code to a better level, I hope... But I'm not sure if this simulation...
This is VHDL code:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Kitchen_Timer is
port (
clk : in std_logic; -- Clock input
reset : in std_logic; -- Reset input
start : in std_logic; -- Start button input
stop : in std_logic; -- Stop button input
adjust_interval_up : in std_logic; -- Button for increasing alarm interval
adjust_interval_down : in std_logic; -- Button for decreasing alarm interval
alarm : out std_logic -- Alarm output
);
end entity Kitchen_Timer;
architecture Behavioral of Kitchen_Timer is
signal count : integer range 0 to 3600000 := 0; -- Adjust range for 1 hour
signal alarming : std_logic := '0';
signal alarm_interval : integer range 600 to 3600000 := 600; -- Adjust range for 1 hour
begin
process (clk, reset)
begin
if reset = '1' then
count <= 0;
alarming <= '0';
alarm_interval <= 600;
elsif rising_edge(clk) then
if start = '1' then
count <= count + 1;
end if;
if stop = '1' or count = alarm_interval then
count <= 0;
end if;
if adjust_interval_up = '1' then
if alarm_interval < 3600000 then
alarm_interval <= alarm_interval + 600; -- Adjust increment for 1 minute
end if;
elsif adjust_interval_down = '1' then
if alarm_interval > 600 then
alarm_interval <= alarm_interval - 600; -- Adjust decrement for 1 minute
end if;
end if;
end if;
end process;
alarming <= '1' when count >= alarm_interval else '0';
alarm <= alarming;
end architecture Behavioral;
This is Testbench:
library ieee;
use ieee.std_logic_1164.all;
entity tb_Kitchen_Timer is
end tb_Kitchen_Timer;
architecture tb of tb_Kitchen_Timer is
component Kitchen_Timer
port (
clk : in std_logic;
reset : in std_logic;
start : in std_logic;
stop : in std_logic;
adjust_interval_up : in std_logic;
adjust_interval_down : in std_logic;
alarm : out std_logic
);
end component;
signal clk : std_logic := '0';
signal reset : std_logic := '0';
signal start : std_logic := '0';
signal stop : std_logic := '0';
signal adjust_interval_up : std_logic := '0';
signal adjust_interval_down : std_logic := '0';
signal alarm : std_logic;
constant TbPeriod : time := 1 us; -- Set the clock period to 1us
signal TbClock : std_logic := '0';
signal TbSimEnded : std_logic := '0';
begin
dut : Kitchen_Timer
port map (
clk => clk,
reset => reset,
start => start,
stop => stop,
adjust_interval_up => adjust_interval_up,
adjust_interval_down => adjust_interval_down,
alarm => alarm
);
-- Clock generation
TbClock <= not TbClock after TbPeriod/2 when TbSimEnded /= '1' else '0';
clk <= TbClock;
stimuli : process
begin
-- Reset generation
reset <= '1';
wait for 20 us; -- Adjust delay to fit the new clock period
reset <= '0';
-- Add your stimuli and test cases here
-- For example:
start <= '1';
wait for 50 us; -- Adjust delay to fit the new clock period
start <= '0';
wait for 400 us; -- Adjust delay to fit the new clock period
adjust_interval_up <= '1';
wait for 20 us; -- Adjust delay to fit the new clock period
adjust_interval_up <= '0';
wait for 50 us; -- Adjust delay to fit the new clock period
adjust_interval_down <= '1';
wait for 20 us; -- Adjust delay to fit the new clock period
adjust_interval_down <= '0';
wait for 50 us; -- Adjust delay to fit the new clock period
-- ...
-- Stop the clock and hence terminate the simulation
TbSimEnded <= '1';
wait;
end process;
end tb;
-- Configuration block below is required by some simulators. Usually no need to edit.
configuration cfg_tb_Kitchen_Timer of tb_Kitchen_Timer is
for tb
end for;
end cfg_tb_Kitchen_Timer;
And this is result of simulation:
@dejo This is much better, but there is still some room for improvement.
There is a mismatch between your comparisons
count
andalarm_interval
. Here in the code bellow you can see the issue:As it is right now, the
alarming
signal is never going to be '1'. It is best to split the comparison and write toalarming
directly:As for the testbench, you should set the start and unset it only after the
alarming
is '1', and test if alarming is working after adjusting the timer:I suggest changing the 100 ms time slices you use in the timer to a minute instead. That way your simulation time could be much quicker (though you would also have to change the testbench delays).
@T4V0 I
In the meantime, I worked on improving the code.
VHDL code:
llibrary ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Kitchen_Timer is
port (
clk : in std_logic; -- Clock input
reset : in std_logic; -- Reset input
start : in std_logic; -- Start button input
stop : in std_logic; -- Stop button input
adjust_interval_up : in std_logic; -- Button for increasing alarm interval
adjust_interval_down : in std_logic; -- Button for decreasing alarm interval
alarm : out std_logic -- Alarm output
);
end entity Kitchen_Timer;
architecture Behavioral of Kitchen_Timer is
signal count : integer range 0 to 3600000 := 0; -- Adjust range for 1 hour
signal alarming : std_logic := '0';
signal alarm_interval : integer range 600 to 3600000 := 600; -- Adjust range for 1 hour
begin
process (clk, reset)
begin
if reset = '1' then
count <= 0;
alarm_interval <= 600;
elsif rising_edge(clk) then
if start = '1' then
count <= count + 1;
end if;
if stop = '1' or count = alarm_interval then
count <= 0;
end if;
if adjust_interval_up = '1' then
if alarm_interval < 3600000 then
alarm_interval <= alarm_interval + 600; -- Adjust increment for 1 minute
end if;
count <= 0; -- Reset count when adjusting interval
elsif adjust_interval_down = '1' then
if alarm_interval > 600 then
alarm_interval <= alarm_interval - 600; -- Adjust decrement for 1 minute
end if;
count <= 0; -- Reset count when adjusting interval
end if;
end if;
end process;
alarming <= '1' when count >= alarm_interval else '0';
alarm <= alarming;
end architecture Behavioral;
Testbench:
library ieee;
use ieee.std_logic_1164.all;
entity tb_Kitchen_Timer is
end tb_Kitchen_Timer;
architecture tb of tb_Kitchen_Timer is
component Kitchen_Timer
port (
clk : in std_logic;
reset : in std_logic;
start : in std_logic;
stop : in std_logic;
adjust_interval_up : in std_logic;
adjust_interval_down : in std_logic;
alarm : out std_logic
);
end component;
signal clk : std_logic := '0';
signal reset : std_logic := '0';
signal start : std_logic := '0';
signal stop : std_logic := '0';
signal adjust_interval_up : std_logic := '0';
signal adjust_interval_down : std_logic := '0';
signal alarm : std_logic;
constant TbPeriod : time := 20 ns;
signal TbClock : std_logic := '0';
signal TbSimEnded : std_logic := '0';
begin
dut : Kitchen_Timer
port map (
clk => clk,
reset => reset,
start => start,
stop => stop,
adjust_interval_up => adjust_interval_up,
adjust_interval_down => adjust_interval_down,
alarm => alarm
);
-- Clock generation
TbClock <= not TbClock after TbPeriod/2 when TbSimEnded /= '1' else '0';
clk <= TbClock;
stimuli : process
variable num_ticks : natural;
begin
-- Reset generation
reset <= '1';
wait for 200 us;
reset <= '0';
wait for 200 us;
-- Start the timer
start <= '1';
wait for 500 us;
-- Adjust interval up and down
adjust_interval_up <= '1';
wait for 100 us;
adjust_interval_up <= '0';
wait for 100 us;
adjust_interval_down <= '1';
wait for 100 us;
adjust_interval_down <= '0';
wait for 100 us;
-- Wait for the timer to reach the alarm interval (3600000 clocks)
wait for 72 ms; -- Simulate for the required time
-- Stop the timer
start <= '0';
wait for 300 us;
-- Stop the clock and terminate the simulation
TbSimEnded <= '1';
wait;
end process;
end tb;
And this is simulation:
@dejo I have made a few changes to your code:
Kitchen_Timer.vhd
tb_Kitchen_Timer.vhd
This should be easier to simulate, I've included a simulation done with Questa.
@T4V0 I just now see your messages, thank you..
In the meantime, I made something like this...
What do you think about the specifications that the project requires, should I stick to your code or should I add something from my own code?
Does your simulation correspond to a time of 1 hour and should there be alarming on the simulation?
Vhdl code:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Kitchen_Timer is
port (
clk : in std_logic; -- Clock input
reset : in std_logic; -- Reset input
start : in std_logic; -- Start button input
stop : in std_logic; -- Stop button input
adjust_interval_up : in std_logic; -- Button for increasing alarm interval
adjust_interval_down : in std_logic; -- Button for decreasing alarm interval
alarm : out std_logic -- Alarm output
);
end entity Kitchen_Timer;
architecture Behavioral of Kitchen_Timer is
signal count : integer range 0 to 3600000 := 0; -- Adjust range for 1 hour
signal alarming : std_logic := '0';
signal alarm_interval : integer range 600 to 3600000 := 600; -- Adjust range for 1 hour
begin
process (clk, reset)
begin
if reset = '1' then
count <= 0;
alarm_interval <= 600;
elsif rising_edge(clk) then
if start = '1' then
count <= count + 1;
end if;
if stop = '1' or count = alarm_interval then
count <= 0;
end if;
if adjust_interval_up = '1' then
if alarm_interval < 3600000 then
alarm_interval <= alarm_interval + 600; -- Adjust increment for 1 minute
end if;
count <= 0; -- Reset count when adjusting interval
elsif adjust_interval_down = '1' then
if alarm_interval > 600 then
alarm_interval <= alarm_interval - 600; -- Adjust decrement for 1 minute
end if;
count <= 0; -- Reset count when adjusting interval
end if;
end if;
end process;
alarming <= '1' when count >= alarm_interval else '0';
alarm <= alarming;
end architecture Behavioral;
Testbench:
library ieee;
use ieee.std_logic_1164.all;
entity tb_Kitchen_Timer is
end tb_Kitchen_Timer;
architecture tb of tb_Kitchen_Timer is
component Kitchen_Timer
port (
clk : in std_logic;
reset : in std_logic;
start : in std_logic;
stop : in std_logic;
adjust_interval_up : in std_logic;
adjust_interval_down : in std_logic;
alarm : out std_logic
);
end component;
signal clk : std_logic := '0';
signal reset : std_logic := '0';
signal start : std_logic := '0';
signal stop : std_logic := '0';
signal adjust_interval_up : std_logic := '0';
signal adjust_interval_down : std_logic := '0';
signal alarm : std_logic;
constant TbPeriod : time := 20 ns;
signal TbClock : std_logic := '0';
signal TbSimEnded : std_logic := '0';
begin
dut : Kitchen_Timer
port map (
clk => clk,
reset => reset,
start => start,
stop => stop,
adjust_interval_up => adjust_interval_up,
adjust_interval_down => adjust_interval_down,
alarm => alarm
);
-- Clock generation
TbClock <= not TbClock after TbPeriod/2 when TbSimEnded /= '1' else '0';
clk <= TbClock;
stimuli : process
variable num_ticks : natural;
begin
-- Reset generation
reset <= '1';
wait for 200 us;
reset <= '0';
wait for 200 us;
-- Start the timer
start <= '1';
wait for 500 us;
-- Adjust interval up and down
adjust_interval_up <= '1';
wait for 100 us;
adjust_interval_up <= '0';
wait for 100 us;
adjust_interval_down <= '1';
wait for 100 us;
adjust_interval_down <= '0';
wait for 100 us;
-- Wait for the timer to reach the alarm interval (3600000 clocks)
wait for 72 ms; -- Simulate for the required time
-- Stop the timer
start <= '0';
wait for 300 us;
-- Stop the clock and terminate the simulation
TbSimEnded <= '1';
wait;
end process;
end tb;
@dejo
I would stick to my code, your alarm isn't going to work properly due to its comparisons as I mentioned in my previous comments. But if you want to improve the code I modified, you can change the
adjust_interval_up
andadjust_interval_down
buttons to be synchronized to their own states rather than the clock (make their own process with their signals added to the signal sensitivity list and add an extra asynchronous condition to zero the counter on the original process). If you don't make a change like this your alarm is going to take up to an hour to adjust its timer range.Yes, if you have a 1/60 Hertz clock signal. And you must have alarming on the simulation as it is crucial to show that it works.
@T4V0
Is the 1/60 Hz set somewhere or is it set in the code itself?
When you say that I must have an "alarming" signal on the simulation, is it actually this "alarm" signal that is presented on the simulation or?
And, do I need to have count signal in simulation?
@dejo
You would set that on the testbench or on your synthesis code, but that is unnecessary, I only said that in case if you tested it on a actual FPGA. If you do that on your testbench, it would take a very long time to simulate.
The
alarm
signal. The "alarming" is when thealarm
signal is in a high logic state.I wouldn't say it's mandatory, but it is a good addition to the simulation, keep it.
@T4V0 How to How can I include the count signal in the simulation so that it is displayed to me
Shouldn't the alarm signal last longer when the alarm interval is increased?
@T4V0
@dejo No, the alarm signal takes a longer time to start rather than the interval when it's set.
You seem to have some trouble picturing the Kitchen Timer itself.
Kitchen timer
Here, when you change the time interval it's the same as when you turn the knob. So it doesn't last longer when you increase the timer, it just takes longer for it to activate.
@T4V0 Thank you so much.
@dejo In your .do script file add this line:
@T4V0 I use Notepad to write scripts.I don't think it works that way with the code written like this
**library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb_Kitchen_Timer is
end tb_Kitchen_Timer;
architecture tb of tb_Kitchen_Timer is
signal clk : std_logic := '0';
signal reset : std_logic := '0';
signal start : std_logic := '0';
signal stop : std_logic := '0';
signal adjust_interval_up : std_logic := '0';
signal adjust_interval_down : std_logic := '0';
signal alarm : std_logic;
constant TbPeriod : time := 10 ns;
signal TbClock : std_logic := '0';
signal TbSimEnded : std_logic := '0';
begin
dut : entity work.Kitchen_Timer
port map
(
clk => clk,
reset => reset,
start => start,
stop => stop,
adjust_interval_up => adjust_interval_up,
adjust_interval_down => adjust_interval_down,
alarm => alarm
)
TbClock <= not TbClock after TbPeriod/2 when TbSimEnded /= '1' else '0'; -- Clock generation
clk <= TbClock;
stimuli : process
variable num_ticks : natural;
begin
-- Reset generation
reset <= '1';
wait for 20 ns;
reset <= '0';
wait for 20 ns;
-- Start the timer
start <= '1';
wait for 20 ns;
start <= '0';
stop <= '1';
-- Adjust interval up and down
adjust_interval_up <= '1';
wait for 10 ns;
start <= '1';
stop <= '0';
adjust_interval_up <= '0';
wait for 30 ns;
start <= '0';
stop <= '1';
adjust_interval_down <= '1';
wait for 10 ns;
start <= '1';
stop <= '0';
adjust_interval_down <= '0';
wait for 20 ns;
start <= '0';
stop <= '1';
adjust_interval_up <= '1';
wait for 600 ns;
start <= '1';
stop <= '0';
adjust_interval_up <= '0';
-- Wait for the timer to reach the alarm interval (60 clocks)
wait for 600 ns; -- Simulate for the required time
-- Stop the timer
start <= '0';
stop <= '1';
wait for 100 ns;
-- Stop the clock and terminate the simulation
TbSimEnded <= '1';
wait;
end process;
end tb;
**
@dejo Whoops, I made a mistake, you don't need to include the entity when simulating. This line bellow should be the correct one:
This is my .do file (when using Modelsim or Questa, change to the directory with all the .vhd files and the .do file and execute the command
do tb.do
):tb.do
I've also included the simulation result.
@T4V0 Тhank you.
@T4V0 Thanks a lot for your help, I appreciate it.