When testing a simple counter implementation, the VHDL simulation is not exiting the simulation. My intention is to stop both concurrent processes using the shared variable changed by the main process. But the main process is not stopping the clock process.
My counter implementation is:
entity dff is
port(
direction, reset, clk, load : in std_logic;
din : in std_logic_vector(3 downto 0);
dout : out std_logic_vector(3 downto 0));
end dff;
architecture behav of dff is
signal temp : std_logic_vector(3 downto 0);
begin
process(clk, reset)
begin
if (reset='1') then
temp <= "0000";
elsif rising_edge(clk) then
if (load='1') then
temp <= din;
else
if (direction='0') then
temp <= std_logic_vector(unsigned(temp) + 1);
else
temp <= std_logic_vector(unsigned(temp) - 1);
end if;
end if;
end if;
dout <= temp;
end process;
end behav;
And my testbench:
architecture behav of test_tb is
component dff port(
direction, reset, clk, load : in std_logic;
din : in std_logic_vector(3 downto 0);
dout : out std_logic_vector(3 downto 0));
end component;
signal direction, reset, clk, load : std_logic := '1';
signal din, dout : std_logic_vector(3 downto 0) := x"7";
shared variable simend : boolean := false;
begin
clkk : process
begin
if simend=false then
clk <= not clk after 50 ns;
else
wait;
end if;
end process clkk;
uut : dff port map(
direction, reset, clk, load, din, dout);
stim : process
begin
reset <= '0';
wait for 1 us;
load <= '0';
wait for 2 us;
direction <= '0';
wait for 2 us;
load <= '1';
wait for 1 us;
reset <= '1';
wait for 0.5 us;
simend := true;
wait;
end process stim;
end behav;
An alternative way to end the simulation if you have a VHDL2008-compliant simulator is to:
use std.env.stop;
you can then end the simulation by calling stop:
stop;
This seems to me to be more elegant than waiting for a lack of clock transitions to cause the simulator iteration limit to be reached.
I would code your clock generator more like this:
clkk : process
begin
while simend=false loop
clk <= not clk;
wait for 50 ns;
end loop;
wait;
end process clkk;
It is possible to execute your clkk process without ever executing a wait statement. (The line clk <= not clk after 50 ns does not wait or block - <= is a non-blocking assignment.) Therefore, you have an infinite loop that will never stop. You can see this by running this example on EDA Playground where the simulation time never advances and, because the maximum runtime on EDA Playground is 1 minute, times out after 1 minute.
Also, I would recommend not using a shared variable for simend. Instead, why not use a signal? You code would not even be compiable in VHDL-2000 onwards, because after VHDL-2000, shared variables had to be protected types. You can see that a warning is produced on EDA Playground unless you set the option to compile VHDL-93. Compiling for VHDL-93, would prevent you using the stop (or finish) procedures.
I agree with #scary_jeff, std.env.stop is a great answer here. If I am just calling it in one place, my preference is to leave off the package reference and just call it:
std.env.stop;
In the event you are stuck with an older simulator, you can use
report "Just Kidding. Test Done." severity failure ;
OTOH if you need to coordinate ending a simulation between multiple processes and add a watch dog timer to your simulation run, you might consider the procedure Osvvm.TbUtilPkg.WaitForBarrier. It is used as shown below. The first call to WaitForBarrier(TestDone, 5 ms) will wake up in 5 ms in the event that TestDone does not happen before then and stop the simulation at that time.
signal TestDone : integer_barrier := 1 ;
ControlProc : process
begin
-- initialize test
SetAlertLogName("Uart1_Rx") ;
. . .
WaitForBarrier(TestDone, 5 ms) ; -- control process uses timeout
AlertIf(now >= 5 ms, "Test finished due to Time Out") ;
ReportAlerts ;
std.env.stop ;
end process ControlProc ;
CpuProc : process
begin
InitDut(. . . )_;
Toggle(CpuReady) ;
-- run numerous Cpu test transactions
. . .
WaitForBarrier(TestDone) ;
wait ;
end process CpuProc ;
UartTxProc : process
Begin
WaitForToggle(CpuReady) ;
-- run numerous Uart Transmit test transactions
. . .
WaitForBarrier(TestDone) ;
wait ;
end process UartTxProc ;
. . .
You can find the OSVVM library at both osvvm.org and on github. There is also a complete user guide in for this package in the download.
Related
I'm new to FPGA design using VHDL and I'm stucked in a problem of testbench simulation: each time I try to simulate my model (which testbench was given by the testbench writer) I get the following error that's driving me crazy:
Error: (vsim-3173) Entity >'C:/intelFPGA_lite/progetto_dsp/simulation/modelsim/rtl_work.progetto_dsp_top_vhd_t>st' has no architecture.
My model is a simple Phase Frequency Detector (PFD) and all I'd like is to simulate its behavior at different input signals.
Following I post my testbench code, the only thing that I changed from the auto-generated code is the addition of the A and B signals (which I made like clock signals with different frequency).
-- Generated on "05/02/2021 16:58:30"
-- Vhdl Test Bench template for design : progetto_dsp_top
--
-- Simulation tool : ModelSim-Altera (VHDL)
--
LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY progetto_dsp_top_vhd_tst IS
END progetto_dsp_top_vhd_tst;
ARCHITECTURE progetto_dsp_top_arch OF progetto_dsp_top_vhd_tst IS
-- constants
constant period_A : time := 10 ps;
constant period_B : time := 20 ps;
-- signals
SIGNAL A : STD_LOGIC := '0';
SIGNAL B : STD_LOGIC := '0';
SIGNAL High : STD_LOGIC : '1';
SIGNAL QA : STD_LOGIC;
SIGNAL QB : STD_LOGIC;
COMPONENT progetto_dsp_top
PORT (
A : IN STD_LOGIC;
B : IN STD_LOGIC;
High : IN STD_LOGIC;
QA : OUT STD_LOGIC;
QB : OUT STD_LOGIC
);
END COMPONENT;
BEGIN
--signal A generation
process
begin
A <= '1';
wait for period_A/2;
A <= '0';
wait for period_A/2;
if end_sim_s = true then
wait; -- end of simulation
end if;
end process;
signal B generation
process
begin
B <= '1';
wait for period_B/2;
B <= '0';
wait for period_B/2;
if end_sim_s = true then
wait; -- end of simulation
end if;
end process;
i1 : progetto_dsp_top
PORT MAP (
-- list connections between master ports and signals
A => A,
B => B,
High => High,
QA => QA,
QB => QB
);
init : PROCESS
-- variable declarations
BEGIN
-- code that executes only once
WAIT;
END PROCESS init;
always : PROCESS
-- optional sensitivity list
-- ( )
-- variable declarations
BEGIN
-- code executes for every event on sensitivity list
WAIT;
END PROCESS always;
END progetto_dsp_top_arch;
Below is a counter that is designed to represent an 8 bit binary number with 8 LEDs, it is being simulated using a test bench, however when running the simulation the output simply shows UU for the led.
Here is the main entity that I wish to test:
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_unsigned.all;
entity Lab_3_Source_File is
generic(N_BITS : integer := 8);
port(
btnd : in STD_LOGIC ;
clk : in STD_LOGIC;
led : out STD_LOGIC_VECTOR(7 downto 0)
);
end Lab_3_Source_File;
architecture counter of Lab_3_Source_File is
signal count: STD_LOGIC_VECTOR(7 downto 0);
begin
process(clk, btnd)
begin
if btnd = '1' then
count <= (others => '0');
elsif rising_edge(clk) then
count <= count + 1;
end if;
end process;
led <= count;
end counter;
Here is the test bench that I have tried to map to the main entity:
use IEEE.STD_LOGIC_1164.ALL;
entity Count_TestBench is
end Count_TestBench;
architecture Behavioral of Count_TestBench is
signal btnd, clk : STD_LOGIC;
signal led : STD_LOGIC_VECTOR(7 downto 0);
begin
UUT : entity work.Lab_3_Source_File port map (btnd => btnd,clk => clk,led => led);
process
begin
btnd<='1';
wait for 1 ns;
btnd<='0';
led<= (others => '0');
for i in 1 to 100 loop
clk<='1';
wait for 10 ns;
clk<='0';
wait for 10 ns;
led<=led;
end loop;
end process;
end Behavioral;
Please could somebody help me understand how to enable the simulation to display the led output incrementing?
EDIT:
Set btnd to 1 with a 1ns wait in the test bench to initialise the led, following the answer from mkrieger1, the led output is still at U following this change.
count is not initialized inside Lab_3_Source_File until btnd is set to '1', which it isn't in the testbench.
Since the led output is driven by count, it is also uninitialized. The uninitialized value of the led output of Lab_3_Source_File is then assigned to the led signal in the testbench.
So, to fix this, you need to set btnd to '1' once for a non-zero duration in the testbench, before setting it to '0' again (otherwise led is held at "00000000" constantly).
I have to test using modelsim this component:
COMPONENT part5
PORT ( CLOCK_50,KEY0,KEY3 : IN STD_LOGIC;
SW: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
HEX3,HEX2,HEX1,HEX0: OUT STD_LOGIC_VECTOR (6 DOWNTO 0);
LEDR : OUT STD_LOGIC_VECTOR (1 DOWNTO 0)
);
END COMPONENT;
It will be implemented on altera DE2.
It is supposed to work with the clock (CLOCK_50),after KEY0 goes to logic level 1, counting clock periods till it reach the numeber inserted on SW(7 DOWNTO 0),at this point it turns on a red led : LEDR. From the time LEDR is ON the four hexadecimal displays (HEX0, HEX1,HEX2 and HEX3) start to count at 1 ms interval. I have to push a button KEY3 (on the DE2 board) as quickly as possible till I reach the value rappresented in SW(7 DOWNTO 0) : the red light turns off and displays stop counting.
I tried with this:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;`
ENTITY tb_part5 IS
END ENTITY;
ARCHITECTURE beha OF tb_part5 IS
COMPONENT part5
PORT ( CLOCK_50,KEY0,KEY3 : IN STD_LOGIC;
SW: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
HEX3,HEX2,HEX1,HEX0: OUT STD_LOGIC_VECTOR (6 DOWNTO 0);
LEDR : OUT STD_LOGIC_VECTOR (1 DOWNTO 0)
);
END COMPONENT;
SIGNAL clk,key,rst: STD_LOGIC:='0';--inputs
SIGNAL switch: STD_LOGIC_VECTOR (7 DOWNTO 0);
SIGNAL led,led1: STD_LOGIC;--outputs
SIGNAL dec0,dec1,dec2,dec3 : STD_LOGIC_VECTOR (6 DOWNTO 0);
BEGIN
switch<="00001010";
PROCESS --clock
BEGIN
clk<='1' AFTER 10 ns ;
clk<='0' AFTER 20 ns ;
END PROCESS;
PROCESS --reset
BEGIN
rst<='0';
WAIT FOR 20 ns;
rst<='1';
WAIT;
END PROCESS;
PROCESS
BEGIN
IF led='1' THEN
key<= '1';
WAIT FOR 20 ns;
key<='0';
WAIT FOR 20 ns;
ELSE
key<='0';
END IF;
END PROCESS;
DUT : part5 PORT MAP (CLOCK_50=>clk, KEY0=>rst,KEY3=>key,SW=>switch,HEX3=>dec3,HEX2=>dec2,HEX1=>dec1,HEX0=>dec0,LEDR(0)=>led,LEDR(1)=>led1);
END beha;
But the simulation don't show any results. I'm not very good at testbenches, I really want to understand how they works, specially with the generation of the clock, and insertion of wave vectors! Maybe I've could explain my doubts better, but if someone could show me a testbench example for beginners it would be very helpful!
Thanks
A beginners architecture for test bench can be really simple. You can test a lot of components with just 5 process (including clk and reset process). When you create a test bench you will usually generate at least one clock and a reset for your design under test (DUT). For those process you can keep the following and this should work for all single clock design (does not matter if reset is synchronous or asynchronous). Then you will create a stimulus process. This process will allow you to generate data for your DUT (you will affect signals connected to your DUT). This process can define the end of your simulation. You can set some signals and wait for 100000 ns if you want to test a sequence without modify signals (DUT inputs).
After generate stimulus you can launch simulation and manually verify your DUT outputs but this in not the best way to do (maybe in your case but not in a bigger design). The easiest way to control the integrity of your outputs is to generate a reference. This reference is the expected reaction of your design. E.g : if you want to realize a design that wait 100 clock cycles. You will create a reference signal of the output but you do not have to use VHDL that you can synthesize. You have access to all VHDL function (wait for, wait until, etc...).
At the end you will have the last process, the checker. This one will compare dut outputs and reference to define if there is some errors or not in your design.
Do not forget to put a wait statement in all process (depending on end_sim_s per exemple) to stop your simulation when everthing has been simulated
Here is an empty test bench structure :
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb_part5 is
port (
-- no IO for test bench
);
end entity;
architecture beha of tb_part5 is
---------------
-- Constants --
---------------
constant CLOCK_PERIOD : time := 10 ns; -- e.g.
------------------------
-- Test bench signals --
------------------------
signal clk_sti : std_logic := '0';
signal rst_sti : std_logic := '1'; -- !!! activ high !!!
-- end of sim flag
signal end_sim_s : boolean := false;
begin
----------------------
-- Clock generation --
----------------------
process
begin
clk_sti <= '1';
wait for CLOCK_PERIOD/2;
clk_sti <= '0';
wait for CLOCK_PERIOD/2;
if end_sim_s = true then
wait; -- end of simulation
end if;
end process;
--------------------
-- Reset sequence --
--------------------
process
begin
rst <= '1';
wait for 2*CLOCK_PERIOD;
rst <= '0';
wait;
end process;
----------------------
-- Stimulus process --
----------------------
process
begin
-- default values for DUT inputs
-- wait end of reset sequence
wait until (rst_sti = '0');
-- do something
-- end of simulation
end_sim_s <= true;
wait;
end process;
-----------------------
-- Reference process --
-----------------------
-------------------
-- Check process --
-------------------
-----------------------
-- DUT instanciation --
-----------------------
end beha;
For your test bench I propose you the following architecture. But you have to be conscious your test bench does not verify anything.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb_part5 is
port (
-- no IO in test bench
);
end entity;
architecture beha of tb_part5 is
---------------
-- Constants --
---------------
constant CLOCK_PERIOD : time := 10 ns; -- e.g.
-----------------------
-- Internals signals --
-----------------------
signal clk, key, rst : std_logic := '0'; --inputs
signal switch : std_logic_vector (7 downto 0);
signal led, led1 : std_logic; --outputs
signal dec0, dec1, dec2, dec3 : std_logic_vector (6 downto 0);
-- test bench signals
signal end_sim_s : boolean := false;
begin
switch <= "00001010";
----------------------
-- Clock generation --
----------------------
process
begin
clk <= '0';
wait for CLOCK_PERIOD/2;
clk <= '1';
wait for CLOCK_PERIOD/2;
if end_sim_s = true then
wait; -- end of simulation
end if;
end process;
--------------------
-- Reset sequence --
--------------------
process
begin
-- TIPS : if you want to be more efficient you should us a norm to
-- define your signal. A reset signal activ low can be called nRst for
-- exemple. Maybe actually you have an activ low reset but maybe not.
-- This exemple show a reset activ low sequence
rst <= '0';
wait for 2*CLOCK_PERIOD;
rst <= '1';
wait;
end process;
---------------------
-- Your TEST bench --
---------------------
-- this part do the same thing that you were asking.
process
begin
-- the if statement in the previous version is not a good thing to do.
-- in fact, you want your process to wait until an event.
-- initial state (default value)
key <= '0';
-- wait until the end of reset sequence (just in case)
wait until (rst = '1'); -- e.g.
-- wait until DUT assert led
wait until (led = '1'); -- e.g.
-- start your sequence
key <= '1';
wait for 20 ns;
key <= '0';
wait for 20 ns;
-- here you have 2 choices. let the process iterate a second time or just
-- end the simulation at this moment
-- stop here
-- notify the others process the end of simulation
end_sim_s <= true;
-- block process
wait;
end process;
-----------------------
-- DUT instanciation --
-----------------------
DUT : part5 port map (
CLOCK_50 => clk,
KEY0 => rst,
KEY3 => key,
SW => switch,
HEX3 => dec3,
HEX2 => dec2,
HEX1 => dec1,
HEX0 => dec0,
LEDR(0) => led,
LEDR(1) => led1
);
end beha;
Hope this will help you.
Regards.
Mike
I have a simple VHDL design and test bench that does not produce the expected output. ISim shows 'U' for all the outputs until the 'running' state is achieved (myState='1'). Then they show 0 and X values. The first PROCESS block should set all outputs to '0' when ENABLE is '0'. The test bench toggles ENABLE 0-1-0 to insure an event triggers the process, but the outputs stay at 'U'. Is the problem in the design, the test, or both?
VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity TestHarness1 is
port (
ADAT_WDCLK : in std_logic;
ADAT_BCLK: in std_logic;
ADAT_OUT12: in std_logic;
ENABLE: in std_logic;
PCM_FS : out std_logic;
PCM_CLK : out std_logic;
PCM_DIN : out std_logic
);
end TestHarness1;
architecture Behavioral of TestHarness1 is
--type state is (STOPPED, RUNNING);
signal tmp : std_logic;
signal myState : std_logic;
begin
PCM_DIN <= tmp;
-- State management process
process (ENABLE, ADAT_WDCLK) begin -- Eval on input changes
if (ENABLE = '0') then
myState <= '0'; --STOPPED;
PCM_FS <= '0'; -- All outputs muted
PCM_CLK <= '0';
tmp <= '0';
else
if (myState = '0' and rising_edge(ADAT_WDCLK)) then
-- Move to running state only at start of a frame
myState <= '1'; --RUNNING;
end if;
end if;
end process;
-- Output process
process (ADAT_WDCLK, ADAT_BCLK, myState) variable counter: integer := 0; begin
-- Only do something if we are in running state, process above
-- sets outputs when stopped.
if (myState = '1') then
-- Pass the clocks through, inverting the bit clock
PCM_FS <= ADAT_WDCLK;
PCM_CLK <= not ADAT_BCLK;
-- Generate fixed bit pattern data '11000101'
if rising_edge(ADAT_WDCLK) then
-- This would happen naturally since there are 4 bytes per word clock
counter := 0;
end if;
if falling_edge(ADAT_WDCLK) then
-- This would happen naturally since there are 4 bytes per word clock
counter := 0;
end if;
if rising_edge(ADAT_BCLK) then -- Change data state only on falling edge of output PCM_CLK
if counter = 0 or counter = 1 or counter = 5 or counter = 7 then
tmp <= '1';
else
tmp <= '0';
end if;
if (counter = 7) then
counter := 0; -- Reset counter
else
counter := counter + 1; -- Just inc counter
end if;
end if;
end if;
end process;
end Behavioral;
Test Bench
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY TH1TestBench3 IS
END TH1TestBench3;
ARCHITECTURE behavior OF TH1TestBench3 IS
-- Component Declaration for the Unit Under Test (UUT)
COMPONENT TestHarness1
PORT(
ADAT_WDCLK : IN std_logic;
ADAT_BCLK : IN std_logic;
ADAT_OUT12 : IN std_logic;
ENABLE : IN std_logic;
PCM_FS : OUT std_logic;
PCM_CLK : OUT std_logic;
PCM_DIN : OUT std_logic
);
END COMPONENT;
--Inputs
signal ADAT_WDCLK : std_logic := '0';
signal ADAT_BCLK : std_logic := '0';
signal ADAT_OUT12 : std_logic := '0';
signal ENABLE : std_logic := '0';
--Outputs
signal PCM_FS : std_logic;
signal PCM_CLK : std_logic;
signal PCM_DIN : std_logic;
-- Clock period definitions. Note WDCLK is defined in terms of the bit clock
-- to insure they are exactly in sync.
constant ADAT_BCLK_period : time := 326 ns; -- About 3.072MHz (https://www.sensorsone.com/frequency-to-period-calculator/)
constant ADAT_WDCLK_period : time := ADAT_BCLK_period * 64; -- 48KHz
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut: TestHarness1 PORT MAP (
ADAT_WDCLK => ADAT_WDCLK,
ADAT_BCLK => ADAT_BCLK,
ADAT_OUT12 => ADAT_OUT12,
ENABLE => ENABLE,
PCM_FS => PCM_FS,
PCM_CLK => PCM_CLK,
PCM_DIN => PCM_DIN
);
-- Clock process definitions
ADAT_WDCLK_process :process
begin
ADAT_WDCLK <= '0';
wait for ADAT_WDCLK_period/2;
ADAT_WDCLK <= '1';
wait for ADAT_WDCLK_period/2;
end process;
ADAT_BCLK_process :process
begin
ADAT_BCLK <= '1';
wait for ADAT_BCLK_period/2;
ADAT_BCLK <= '0';
wait for ADAT_BCLK_period/2;
end process;
-- Stimulus process
stim_proc: process
begin
-- hold reset state for 100 ns.
wait for 100 ns;
ENABLE <= '1';
wait for 100 ns;
ENABLE <= '0';
wait for 7500 ns;
ENABLE <= '1';
wait for ADAT_WDCLK_period*10;
-- insert stimulus here
wait;
end process;
END;
ISim shows the ENABLE pulse early in the simulation, but the outputs remain 'U' until the rising edge of the WCLK with ENABLE=1. Then they start to change (as designed) but they show some X values.
Modified VHDL
For reference, here is the modified VHDL that resolves the problem of U's and X's in the simulation output. However, there is a functional problem with the PCM_DIN output... seems like it is delayed one (BCLK) cycle. I expected it to be '1' as soon as ADAT_WDCLK goes high the first time after ENABLE. But it does not go to '1' until a BLCK cycle later.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity TestHarness1 is
port (
ADAT_WDCLK : in std_logic;
ADAT_BCLK: in std_logic;
ADAT_OUT12: in std_logic;
ENABLE: in std_logic;
PCM_FS : out std_logic;
PCM_CLK : out std_logic;
PCM_DIN : out std_logic
);
end TestHarness1;
architecture Behavioral of TestHarness1 is
--type state is (STOPPED, RUNNING);
signal tmp : std_logic;
signal myState : std_logic;
begin
PCM_DIN <= tmp;
-- State management process
process (ENABLE, ADAT_WDCLK) begin -- Eval on input changes
if (ENABLE = '0') then
myState <= '0'; --STOPPED;
else
if (myState = '0' and rising_edge(ADAT_WDCLK)) then
-- Move to running state only at start of a frame
myState <= '1'; --RUNNING;
end if;
end if;
end process;
-- Output process
process (ADAT_WDCLK, ADAT_BCLK, myState) variable counter: integer := 0; begin
-- Only do something if we are in running state
if (myState = '0') then
PCM_FS <= '0'; -- All outputs muted
PCM_CLK <= '0';
tmp <= '0';
elsif (myState = '1') then
-- Pass the clocks through, inverting the bit clock
PCM_FS <= ADAT_WDCLK;
PCM_CLK <= not ADAT_BCLK;
if rising_edge(ADAT_BCLK) then -- Generate fixed serial bit pattern
if counter = 0 or counter = 1 or counter = 5 or counter = 7 then
tmp <= '1';
else
tmp <= '0';
end if;
if (counter = 7) then
counter := 0; -- Reset counter
else
counter := counter + 1; -- Just inc counter
end if;
end if;
end if;
end process;
end Behavioral;
ISim of the above (including the internal myState signal)... why is PCM_DIN delayed one BCLK cycle?
Regarding the 'X' (Forcing Unknown) values you are seeing:
You are driving the signals PCM_FS, PCM_CLK and tmp from multiple processes, which results in the simulator being unable to resolve the value being driven. You need to fix this such that they are only being driven from one process, or drive 'Z' when they are not in use.
Regarding the 'U' values, they exist because you have no initial values for the signals. Once you write the signals for the first time (after the enable), they will be assigned for the first time.
I'm having some trouble implementing a ring oscillator. I don't care about it working on an FPGA. I only want to simulate using Xilinx ISE. Is the code below acceptable? I also addded the test bench. Thanks!
Code
library ieee;
use ieee.std_logic_1164.all;
-- 5 Ring Oscillator
entity ring_osc is
port (ro_en : in std_logic;
delay : in time;
ro_out : out std_logic);
end ring_osc;
architecture behavioral of ring_osc is
signal gate_out : std_logic_vector(5 downto 0) := (others => '0');
begin
process
begin
gate_out(0) <= ro_en and gate_out(5);
wait for delay;
gate_out(1) <= not(gate_out(0));
wait for delay;
gate_out(2) <= not(gate_out(1));
wait for delay;
gate_out(3) <= not(gate_out(2));
wait for delay;
gate_out(4) <= not(gate_out(3));
wait for delay;
gate_out(5) <= not(gate_out(4));
wait for delay;
ro_out <= gate_out(5);
end process;
end behavioral;
Test Bench
library ieee;
use ieee.std_logic_1164.all;
entity ring_osc_tb is
end ring_osc_tb;
architecture behavior of ring_osc_tb is
-- component declaration for the unit under test (uut)
component ring_osc
port (ro_en : in std_logic;
delay : in time;
ro_out : out std_logic);
end component;
-- Inputs
signal ro_en : std_logic := '0';
signal delay : time := 0.5 ns;
-- Outputs
signal ro_out : std_logic;
signal clk : std_logic := '0';
constant clk_period : time := 10 ns;
begin
-- instantiate the unit under test (uut)
uut: ring_osc port map (
ro_en => ro_en,
delay => delay,
ro_out => ro_out
);
-- clock process definitions
clk_process :process
begin
clk <= '0';
wait for clk_period/2;
clk <= '1';
wait for clk_period/2;
end process;
-- stimulus process
stim_proc: process
begin
ro_en <= '1';
delay <= 0.5 ns;
wait for 10*clk_period;
delay <= 1 ns;
wait for 5*clk_period;
assert false report "End of Simulation" severity failure;
end process;
end;
The process with sequential wait does not describe the concurrent nature of the gates in the Ring oscillator, since execution is suspended for delay time at each wait, which is not the way a real word design operates.
A description with concurrent evaluation of all the gates can be:
gate_out(0) <= ro_en and gate_out(5) after delay;
inv_g : for i in 1 to gate_out'high generate
gate_out(i) <= not gate_out(i - 1) after delay;
end generate;
ro_out <= gate_out(5);
This is for simulation only, as also noted in the question, due to the inherent loop nature of a ring oscillator.
Using test bench, with added disable at start:
-- Disable at start
ro_en <= '0';
delay <= 0.5 ns;
wait for 10 * 0.5 ns;
Then the resulting waveform is: