Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Circular buffer design

Status
Not open for further replies.

design_newbie

Newbie level 5
Joined
Apr 29, 2019
Messages
10
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Location
Canada
Activity points
354
Hi all,

I am trying to design a circular buffer for a project, the project includes couple of modules and the way it should work is to receive a transport packet (188 bytes - which it receives the packet byte by byte) then in circular buffer when I receive 188 byte , the data_ready flag goes up and the next module will get the transport packet. I read the concept of circular buffer in Wikipedia and started my design.

Here is my code (I am using vivado 2018.3 for my design)

Code:
[syntax=vhdl]library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity circular_buffer is
    generic(
        DATA_WIDTH : integer := 8;
        ADDR_WIDTH : integer := 12 -- 2^12 = 4096 = 4k
    );
    Port ( 
        -- Input clock domain
        clk          : in  STD_LOGIC;
        rstn        : in  STD_LOGIC;
        wr_en     : in  STD_LOGIC;
        wr_addr  : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_in   : in  STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
        
        -- Output clock domain
        -- s_clk       : in  STD_LOGIC;
        rd_en       : in  STD_LOGIC;
        rd_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_out    : out STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
	full	    : out STD_LOGIC;
	empty	    : out STD_LOGIC;
        data_ready  : out STD_LOGIC
        
        );
end circular_buffer;

architecture Behavioral of circular_buffer is

constant ts_packet_size : integer := 188;
signal byte_count 	: unsigned(ts_packet_size-1 downto 0) := (others => '0');
signal fifo_empty 	: std_logic := '1';
signal fifo_full  	: std_logic := '0';
signal rd_ptr   	: unsigned(ADDR_WIDTH-1 downto 0) := (others => '0');
signal wr_ptr   	: unsigned(ADDR_WIDTH-1 downto 0) := (others => '0');

type memory_type is array (0 to ADDR_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);

signal memory : memory_type := (others => (others => '0'));

begin

full<= fifo_full;
empty <= fifo_empty;

process(clk, rstn)
begin
    if rstn = '0' then
        memory <= (others => (others => '0'));
        rd_ptr <= (others => '0');
        wr_ptr <= (others => '0');
        fifo_full   <= '0';
        fifo_empty  <= '1';
	data_ready  <= '0';
    elsif rising_edge(clk) then
        -- Write
        if (wr_en = '1' and fifo_full = '0') then
            memory(to_integer(wr_ptr)) <= data_in;
            wr_ptr <= wr_ptr + 1;
	    byte_count <= byte_count + 1;
	    -- One packet is ready
	    if(byte_count = ts_packet_size) then
		data_ready <= '1';
	    else
		data_ready <= '0';
	    end if;
        end if;
        -- Read
        if ( rd_en = '1' and fifo_empty = '1') then
            data_out <= memory(to_integer(rd_ptr));
            rd_ptr <= rd_ptr - 1;
        end if;
        -- Memory full
        if (( wr_ptr + 1 = rd_ptr ) and (wr_en = '1') and (rd_en = '0')) then
            fifo_full <= '1';
        else
            fifo_full <= '0';
        end if;
        -- Memory empty
        if ((rd_ptr = wr_ptr) and (rd_en = '1') and (wr_en = '0')) then
            fifo_empty <= '0';
        else
            fifo_empty <= '1';
        end if;
    end if;
        
end process;

end Behavioral;[/syntax]


I wrote a testbench to see if my logic works. in the testbench first i tried a single write and a single read which seems working fine.Afterwards I tried to write in all locations of the RAM but it does not proceed after writing in first 11 locations ! Then I checked where does it stuck and it seems that the process stopped at line 74 of my code which is writing the data into the memory. I can't see the problem and I am a beginner. I tried to google it and even search here but I was not able to find a solution or improve my design !

Thank you in advance
 

Code:
if ( rd_en = '1' and [B]fifo_empty = '1'[/B]) then
            data_out <= memory(to_integer(rd_ptr));
            rd_ptr <= rd_ptr - 1;
        end if;

You want to output data from the memory when the FIFO is empty?
You should read when FIFO is not empty.
 
Hello dpaul,

Thank you for pointing that out. I fixed that so we read when read enable is 1 and fifo is not empty.

Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity circular_buffer is
    generic(
        DATA_WIDTH : integer := 8;
        ADDR_WIDTH : integer := 12 -- 2^12 = 4096 = 4k
    );
    Port ( 
        -- Input clock domain
        clk         : in  STD_LOGIC;
        rstn        : in  STD_LOGIC;
        wr_en       : in  STD_LOGIC;
        wr_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_in     : in  STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
        
        -- Output clock domain
        -- s_clk       : in  STD_LOGIC;
        rd_en       : in  STD_LOGIC;
        rd_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_out    : out STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
	full	    : out STD_LOGIC;
	empty	    : out STD_LOGIC;
        data_ready  : out STD_LOGIC
        
        );
end circular_buffer;

architecture Behavioral of circular_buffer is

constant ts_packet_size : integer := 188;
signal byte_count 	: unsigned(ts_packet_size-1 downto 0) := (others => '0');
signal fifo_empty 	: std_logic := '1';
signal fifo_full  	: std_logic := '0';
signal rd_ptr   	: unsigned(ADDR_WIDTH-1 downto 0) := (others => '0');
signal wr_ptr   	: unsigned(ADDR_WIDTH-1 downto 0) := (others => '0');

type memory_type is array (0 to ADDR_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);

signal memory : memory_type := (others => (others => '0'));

begin

full	<= fifo_full;
empty 	<= fifo_empty;

process(clk, rstn)
begin
    if rstn = '0' then
        memory <= (others => (others => '0'));
        rd_ptr <= (others => '0');
        wr_ptr <= (others => '0');
        fifo_full   <= '0';
        fifo_empty  <= '1';
	data_ready  <= '0';
    elsif rising_edge(clk) then
        -- Write
        if (wr_en = '1' and fifo_full = '0') then
            memory(to_integer(wr_ptr)) <= data_in;
            wr_ptr <= wr_ptr + 1;
	    byte_count <= byte_count + 1;
	    -- One packet is ready
	    if(byte_count = ts_packet_size) then
		data_ready <= '1';
	    else
		data_ready <= '0';
	    end if;
        end if;
        -- Read ( If read enable is 1 and fifo is not empty)
        if ( rd_en = '1' and fifo_empty = '0') then
            data_out <= memory(to_integer(rd_ptr));
            rd_ptr <= rd_ptr - 1;
        end if;
        -- Memory full
        if (( wr_ptr + 1 = rd_ptr ) and (wr_en = '1') and (rd_en = '0')) then
            fifo_full <= '1';
        else
            fifo_full <= '0';
        end if;
        -- Memory empty
        if ((rd_ptr = wr_ptr) and (rd_en = '1') and (wr_en = '0')) then
            fifo_empty <= '1';
        else
            fifo_empty <= '0';
        end if;
    end if;
        
end process;

end Behavioral;


I also have my testbench here :

Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity tb_circular_buffer is
end tb_circular_buffer;

architecture tb of tb_circular_buffer is

component circular_buffer
    generic(
        DATA_WIDTH  : integer := 8;
        ADDR_WIDTH  : integer := 12
    );
    port (
        clk        : in  std_logic;
        rstn       : in  std_logic;
        wr_en      : in  std_logic;
        wr_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_in    : in  std_logic_vector (DATA_WIDTH-1 downto 0);
        rd_en      : in  std_logic;
        rd_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_out   : out std_logic_vector (DATA_WIDTH-1 downto 0);
	full       : out std_logic;
        empty      : out std_logic;
        data_ready : out std_logic
    );
end component;

constant DWIDTH   : integer := 8;
constant AWIDTH   : integer := 12;

signal clk        : std_logic;
signal rstn       : std_logic;
signal wr_en      : std_logic;
signal wr_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_in    : std_logic_vector (DWIDTH-1 downto 0);
signal rd_en      : std_logic;
signal rd_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_out   : std_logic_vector (DWIDTH-1 downto 0);
signal full	  : std_logic;
signal empty	  : std_logic;
signal data_ready : std_logic;

constant clk_period : time := 10 ns;

begin

    dut : circular_buffer
    port map (clk        => clk,
              rstn       => rstn,
              wr_en      => wr_en,
              wr_addr    => wr_addr,
              data_in    => data_in,
              rd_en      => rd_en,
              rd_addr    => rd_addr,
              data_out   => data_out,
	      full	 => full,
	      empty	 => empty,
              data_ready => data_ready);

   -- Clock generation
   clk_process :process
   begin
        clk <= '1';
        wait for clk_period/2;
        clk <= '0';
        wait for clk_period/2;
   end process;


    stimuli : process
    begin
	-- intial state
        wr_en <= '0';
        wr_addr <= (others => '0');
        data_in <= (others => '0');
        rd_en <= '0';
        rd_addr <= (others => '0');


        -- Reset generation
        rstn <= '0';
        wait for 20 ns;
        rstn <= '1';
        wait for 20 ns;

        -- single write
	wr_en 	<= '1';
	data_in <= x"FF";
	wr_addr	<= x"001";
	wait for 10 ns;
	wr_en 	<= '0';
	
	-- single read
	rd_en <= '1';
	rd_addr <= x"001";
	wait for 10 ns;
	rd_en <= '0';
	
	wr_en 	<= '1';
	-- write to all locations
	for i in 1 to 4096 loop
		data_in <= conv_std_logic_vector(i,8);
		wr_addr <= conv_std_logic_vector(i-1,12);
		wait for 10 ns; 
	end loop;
	wr_en <= '0';	


        wait;
    end process;

end tb;
 
Last edited:

This is where you need to study the waveforms and follow the the waves and see if things are happening as you expect. Your question is too vague and non-specific for us to help. You dont even specify what the problem is. If its halting at a specific place (its not, because this is not software) it will be because your input stimulus prevent the design progressing to a different state.
Try and create a test case, and provide it here, and explain exactly what the problem is.

PS. Do NOT reset the memory, otherwise it will be impossible to infer a bram.
 

Hi TrickyDicky,

I need to design a circular buffer which write/read data byte by byte. The input of the circular buffer is MPEG transport stream packets and when it receives a complete transport packet (188 bytes) it will tell the rest of the system that data is ready (data_ready flag should go high) so then it sends out a complete packet out.

Based on my understanding from what I read so far the main functionality of circular buffer should be as following :
Write: When write enable is 1 and memory is not full
Read: When read enable is 1 and memory is not empty
Conditions:
- if write pointer is equal to ( read pointer - 1 ) and you try to write then we know that memory is full so the full flag should go 1
- if write and read pointers point to the same spot and you try to read , memory is empty so there is nothing to read then the empty flag should be 1

Now I have couple of issues which I see in the waveform after I wrote the testbench :
Why the empty flag goes low right after reset is de-asserted and not at the time that I do the first write
What stops the consecutive writes ? Is it in my test or in the design ? ( How should I debug this )
How come in my test I can do single write but when I try to write to all locations, my code stops here : memory(to_integer(wr_ptr)) <= data_in;

Here is my waveform:
tb_circular_buffer_wave.JPG
 

an initial write is occurring right at the falling edge of the reset because you have a race condition with the wr_en going active. You placed the wr_en transition at a time that is on a rising edge of the clock, due to the event scheduling you end up with it being caught by the clock. delay the wr_en by 1 ns and it won't do the write on the reset falling edge.
 

For the fifo empty:


Code VHDL - [expand]
1
2
3
4
5
6
-- Memory empty
        if ((rd_ptr = wr_ptr) and (rd_en = '1') and (wr_en = '0')) then
            fifo_empty <= '1';
        else
            fifo_empty <= '0';
        end if;



Here, FIFO empty goes low because rd_en = 0. You will need to reconsider your empty conditions, probably with an elsif rather than else.

Also - you have issues with the timing in the testbench. You are providing stimulus after absolute delays. THis means the signals actually change just before the clock edge, meaning it looks like there is 0 delay on the waveform. Instead of using wait statements with time, wait for a clock edge instead.


Code VHDL - [expand]
1
2
3
for i in 1 to 5 loop -- wait for 5 clocks
  wait until rising_edge(clk);
end loop;



It may mean less confusion.
 
Hi TrickyDicky,

I modified my design a bit and changed the conditions for memory empty. So now I am able to see in the waveform that the empty signal goes low when the write starts which is correct.


Code:
[syntax=vhdl]library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity circular_buffer is
    generic(
        DATA_WIDTH : integer := 8;
        ADDR_WIDTH : integer := 12 -- 2^12 = 4096 = 4k
    );
    Port ( 
        -- Input clock domain
        clk         : in  STD_LOGIC;
        rstn        : in  STD_LOGIC;
        wr_en       : in  STD_LOGIC;
        wr_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_in     : in  STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
        
        -- Output clock domain
        -- s_clk       : in  STD_LOGIC;
        rd_en       : in  STD_LOGIC;
        rd_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_out    : out STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
	full	    : out STD_LOGIC;
	empty	    : out STD_LOGIC;
	error	    : out STD_LOGIC;
        data_ready  : out STD_LOGIC
        
        );
end circular_buffer;

architecture Behavioral of circular_buffer is

constant ts_packet_size : integer := 188;
signal byte_count 	: unsigned(ts_packet_size-1 downto 0) := (others => '0');
signal fifo_empty : std_logic := '1';
signal fifo_full  : std_logic := '0';
signal ready	: std_logic := '0';
signal rd_ptr   : unsigned(ADDR_WIDTH-1 downto 0) := (others => '0');
signal wr_ptr   : unsigned(ADDR_WIDTH-1 downto 0) := (others => '0');

type memory_type is array (0 to ADDR_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);

signal memory : memory_type := (others => (others => '0'));

begin

full	<= fifo_full;
empty 	<= fifo_empty;
data_ready <= ready;


error <= '1' when (fifo_empty='1' and rd_en='1') or (fifo_full='1' and wr_en='1') else '0';


process(clk, rstn)
begin
    if rstn = '0' then
        memory <= (others => (others => '0'));
        rd_ptr <= (others => '0');
        wr_ptr <= (others => '0');
        fifo_full   <= '0';
        fifo_empty  <= '1';
	ready  <= '0';
    elsif rising_edge(clk) then

        -- Write
        if (wr_en = '1' and fifo_full = '0') then
            memory(to_integer(wr_ptr)) <= data_in;
            wr_ptr <= wr_ptr + 1;
	    byte_count <= byte_count + 1;
	    -- One packet is ready
	   if (byte_count = ts_packet_size) then
		ready <= '1';
	   else
		ready <= '0';
	   end if;
        end if;

        -- Read
        if ( rd_en = '1' and fifo_empty = '0' and ready = '1') then
            data_out <= memory(to_integer(rd_ptr));
            rd_ptr <= rd_ptr - 1;
        end if;

        -- Memory full
        if (( wr_ptr + 1 = rd_ptr ) and (wr_en = '1') and (rd_en = '0')) then
            fifo_full <= '1';
        else
            fifo_full <= '0';
        end if;

        -- Memory empty
        if ((rd_ptr = wr_ptr) and (rd_en = '1') and (wr_en = '0')) then	
            fifo_empty <= '1';

	elsif ((rd_ptr /= wr_ptr) and (rd_en = '1') and (wr_en = '0')) then	-- if we read but write and read pointers do not point to the same location
	    fifo_empty <= '0';

        elsif (( rd_en = '0') and (wr_en = '1') and (fifo_full = '0')) then	-- if we don't read and we write and the fifo is not full
            fifo_empty <= '0';
        end if;
    end if;
        
end process;

end Behavioral;[/syntax]

Regarding the testbench and timing I modified the testbench to use clock edges instead of absolute values but I am not sure if I am using wait until rising_edge(clk) in a correct way in my testbench or not.

Code:
[syntax=vhdl]
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;

entity tb_circular_buffer is
end tb_circular_buffer;

architecture tb of tb_circular_buffer is

component circular_buffer
    generic(
        DATA_WIDTH  : integer := 8;
        ADDR_WIDTH  : integer := 12
    );
    port (
        clk        : in  std_logic;
        rstn       : in  std_logic;
        wr_en      : in  std_logic;
        wr_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_in    : in  std_logic_vector (DATA_WIDTH-1 downto 0);
        rd_en      : in  std_logic;
        rd_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_out   : out std_logic_vector (DATA_WIDTH-1 downto 0);
	full       : out std_logic;
        empty      : out std_logic;
	error      : out std_logic;
        data_ready : out std_logic
    );
end component;

constant DWIDTH   : integer := 8;
constant AWIDTH   : integer := 12;

signal clk        : std_logic;
signal rstn       : std_logic;
signal wr_en      : std_logic;
signal wr_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_in    : std_logic_vector (DWIDTH-1 downto 0);
signal rd_en      : std_logic;
signal rd_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_out   : std_logic_vector (DWIDTH-1 downto 0);
signal full	  : std_logic;
signal empty	  : std_logic;
signal error	  : std_logic;
signal data_ready : std_logic;

constant clk_period : time := 8 ns; --125MHz

begin

    dut : circular_buffer
    port map (clk        => clk,
              rstn       => rstn,
              wr_en      => wr_en,
              wr_addr    => wr_addr,
              data_in    => data_in,
              rd_en      => rd_en,
              rd_addr    => rd_addr,
              data_out   => data_out,
	      full	 => full,
	      empty	 => empty,
	      error	 => error,
              data_ready => data_ready);

    -- Clock generation
    clk_process : process
    begin
        clk <= '1';
        wait for clk_period/2;
        clk <= '0';
        wait for clk_period/2;
    end process;

    stimuli : process
    begin
	-- intial state
        wr_en <= '0';
        wr_addr <= (others => '0');
        data_in <= (others => '0');
        rd_en <= '0';
        rd_addr <= (others => '0');

    	-- Reset generation
        rstn <= '0';
        wait for clk_period;
        rstn <= '1';
        wait for clk_period;

        -- single write
	wr_en 	<= '1';
	wait until rising_edge(clk);
	data_in <= x"FF";
	wr_addr	<= x"001";
	wait until rising_edge(clk);
	wr_en	<= '0';

	--rd_en 	<= '1';
	--rd_addr	<= x"001";
	--wait until rising_edge(clk);
	--rd_en	<= '0';
	
	-- write to all locations
	for i in 1 to 4096 loop
		wr_en 	<= '1';
		data_in <= conv_std_logic_vector(i,8);
		wr_addr <= conv_std_logic_vector(i-1,12);
		wait until rising_edge(clk);
		wr_en 	<= '0';
	end loop;
	

        wait;
    end process;

end tb;[/syntax]

Lastly, I still have the issue of consecutive writes. I played with the following part of my testbench to see why after a certain number of writes the process stops at line 73 .
Well if I take out the we_en <= '1' out of the for loop it will write to all locations but the wr_en signal goes low after one cycle which makes sense based on code but it is not what I need. wr_en should be high as long as there is a write happening.
If I keep we_en <= '1' inside the loop I have the wr_en signal high but it stops after 11 writes.
I moved we_en <= '0' out of the loop so my logic was to make write enable high, write for 4096 times then come out of the loop and change write enable to low. but it does not change anything in the behavior.

Code:
[syntax=vhdl]	-- write to all locations
	for i in 1 to 4096 loop
		wr_en 	<= '1';
		data_in <= conv_std_logic_vector(i,8);
		wr_addr <= conv_std_logic_vector(i-1,12);
		wait until rising_edge(clk);
		wr_en 	<= '0';
	end loop;
[/syntax]

Here is the waveform:
tb_circular_buffer_wave.JPG

Would you please help me with that ?
 
Last edited:

my code stops here
Yes, the simulator stops with a message, it tells you which code error causes the stop. Learn to read the simulator output.

In this case, the reason is wrong range of memory array. Instead of

Code VHDL - [expand]
1
type memory_type is array (0 to ADDR_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);


you should write

Code VHDL - [expand]
1
type memory_type is array (0 to 2**ADDR_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);

 
I modified the code. So I have the write operation happening and also I have the flag data_ready that goes high every 188 Bytes(after receiving a complete transport stream packet).

Now I have some other issues:

1. I have a packet counter so it counts the data that is written to the circular buffer and when it reaches 188 bytes then the ready_data flag should go high. When I check the waveform , when counter reaches 1504 (=188*8) there is a clock cycle that counter has no value which i do not understand why ? So I believe because of that cycle on the next cycle of reading ( if read operation works !) I already missed one data which is E0 that is written to this address "5DF" (Please see the waveform in the attachment)

2. When data_ready flag goes high I need the read operation start reading the data while it keeps writing the rest of the data in the buffer but when the flag goes high, the read operation does not start. I checked the condition for reading operation and I can't see the issue.

3. In the testbench I have a single write at the beginning but then I override the value of that address before reading the value . How can I avoid overriding ? How can I say if there is already something in that address you can't write there ?



Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity circular_buffer is
    generic(
        DATA_WIDTH : integer := 8;
        ADDR_WIDTH : integer := 12 -- 2^12 = 4096 = 4k
    );
    Port ( 
        -- Input clock domain
        clk         : in  STD_LOGIC;
        rstn        : in  STD_LOGIC;
        wr_en       : in  STD_LOGIC;
        wr_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_in     : in  STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
        
        -- Output clock domain
        -- s_clk       : in  STD_LOGIC;
        rd_en       : in  STD_LOGIC;
        rd_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_out    : out STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
        full        : out STD_LOGIC;
        empty       : out STD_LOGIC;
        error       : out STD_LOGIC;
        data_ready  : out STD_LOGIC
        
        );
end circular_buffer;
 
architecture Behavioral of circular_buffer is
 
constant ts_packet_size : integer := 188;
--signal byte_count     : unsigned(DATA_WIDTH-1 downto 0)     := (others => '0');
signal byte_count   : integer range 1 to DATA_WIDTH := 1 ;
signal packet_count : unsigned((8*ts_packet_size)-1 downto 0) := (others => '0');
signal fifo_full  : std_logic := '0';
signal fifo_empty : std_logic := '0';
signal ready    : std_logic := '0';
signal byte_flag: std_logic := '0';
signal rd_ptr   : unsigned(ADDR_WIDTH-1 downto 0) := to_unsigned(0, ADDR_WIDTH);
signal wr_ptr   : unsigned(ADDR_WIDTH-1 downto 0) := to_unsigned(0, ADDR_WIDTH);
 
type memory_type is array (0 to 2**ADDR_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
 
signal memory : memory_type := (others => (others => '0'));
 
begin
 
full    <= fifo_full;
empty   <= fifo_empty;
data_ready <= ready;
 
 
error <= '1' when (fifo_empty='1' and rd_en='1') or (fifo_full='1' and wr_en='1') else '0';
 
 
process(clk, rstn)
begin
    if rstn = '0' then
        memory      <= (others => (others => '0'));
        rd_ptr      <= to_unsigned(0, ADDR_WIDTH);
        wr_ptr      <= to_unsigned(0, ADDR_WIDTH);
    byte_count  <= 1;
    packet_count<= (others => '0');
        fifo_full   <= '0';
        fifo_empty  <= '1';
    ready       <= '0';
        byte_flag   <= '0';
 
    elsif rising_edge(clk) then
 
        -- Write
        if (wr_en = '1' and fifo_full = '0') then
            memory(to_integer(wr_ptr)) <= data_in;
            wr_ptr <= wr_ptr + 1;
    
        -- One packet is ready or not           
        if (packet_count = (8*ts_packet_size)) then
        ready <= '1';
        packet_count <= (others => '0');
        else
        ready <= '0';   
        packet_count <= packet_count + 1;   
            end if;
 
--           -- one byte counter
--      if(byte_count < DATA_WIDTH) then
--          byte_flag <= '0';
--          byte_count <= byte_count + 1;       
--      else
--          byte_count <= 1;
--          byte_flag <= '1';
--      end if;
         end if;
 
 
        -- Read
    -- We read after having one complete transport stream packet
        if ( rd_en = '1' and fifo_empty = '0' and ready = '1') then
            data_out <= memory(to_integer(rd_ptr));
            rd_ptr <= rd_ptr - 1;
        end if;
 
        -- Memory full
        if (( wr_ptr + 1 = rd_ptr ) and (wr_en = '1') and (rd_en = '0')) then
            fifo_full <= '1';
        else
            fifo_full <= '0';
        end if;
 
        -- Memory empty
        if ((rd_ptr = wr_ptr) and (rd_en = '1') and (wr_en = '0')) then 
            fifo_empty <= '1';
 
    elsif ((rd_ptr /= wr_ptr) and (rd_en = '1') and (wr_en = '0')) then -- if we read but write and read pointers do not point to the same location
            fifo_empty <= '0';
 
        elsif (( rd_en = '0') and (wr_en = '1') and (fifo_full = '0')) then -- if we don't read and we write and the fifo is not full
            fifo_empty <= '0';
        end if;
    end if;
        
end process;
 
end Behavioral;



And here is the testbench :


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
 
entity tb_circular_buffer is
end tb_circular_buffer;
 
architecture tb of tb_circular_buffer is
 
component circular_buffer
    generic(
        DATA_WIDTH  : integer := 8;
        ADDR_WIDTH  : integer := 12
    );
    port (
        clk        : in  std_logic;
        rstn       : in  std_logic;
        wr_en      : in  std_logic;
        wr_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_in    : in  std_logic_vector (DATA_WIDTH-1 downto 0);
        rd_en      : in  std_logic;
        rd_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_out   : out std_logic_vector (DATA_WIDTH-1 downto 0);
    full       : out std_logic;
        empty      : out std_logic;
    error      : out std_logic;
        data_ready : out std_logic
    );
end component;
 
constant DWIDTH   : integer := 8;
constant AWIDTH   : integer := 12;
 
signal clk        : std_logic;
signal rstn       : std_logic;
signal wr_en      : std_logic;
signal wr_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_in    : std_logic_vector (DWIDTH-1 downto 0);
signal rd_en      : std_logic;
signal rd_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_out   : std_logic_vector (DWIDTH-1 downto 0);
signal full   : std_logic;
signal empty      : std_logic;
signal error      : std_logic;
signal data_ready : std_logic;
 
constant clk_period : time := 8 ns; --125MHz
 
begin
 
    dut : circular_buffer
    port map (clk        => clk,
              rstn       => rstn,
              wr_en      => wr_en,
              wr_addr    => wr_addr,
              data_in    => data_in,
              rd_en      => rd_en,
              rd_addr    => rd_addr,
              data_out   => data_out,
          full   => full,
          empty  => empty,
          error  => error,
              data_ready => data_ready);
 
    -- Clock generation
    clk_process : process
    begin
        clk <= '1';
        wait for clk_period/2;
        clk <= '0';
        wait for clk_period/2;
    end process;
 
    stimuli : process
    begin
    -- intial state
        wr_en <= '0';
        wr_addr <= (others => '0');
        data_in <= (others => '0');
        rd_en <= '0';
        rd_addr <= (others => '0');
 
        -- Reset generation
        rstn <= '0';
        wait for clk_period;
        rstn <= '1';
        wait for clk_period;
 
        -- single write
    wait for clk_period;
    wr_en   <= '1';
    wait until rising_edge(clk);
    data_in <= x"FF";
    wr_addr <= x"001";
    wait until rising_edge(clk);
    wr_en   <= '0';
 
    -- write to all locations
    for i in 1 to 4096 loop
        wr_en   <= '1';
        data_in <= conv_std_logic_vector(i,8);
        wr_addr <= conv_std_logic_vector(i-1,12);
        wait until rising_edge(clk);
            wr_en   <= '0';
    end loop;
 
    -- read
    for i in 1 to 188 loop
        rd_en <= '1';
        rd_addr <= conv_std_logic_vector(i-1,12);
        wait until rising_edge(clk);
        rd_en <= '0';
    end loop;
 
        wait;
    end process;
 
end tb;



Waveform showing the first time data_ready flag goes high :
tb_circular_buffer.JPG
 
Last edited by a moderator:

Why do you have this defined with a 1504-bit wide counter!?

signal packet_count : unsigned((8*ts_packet_size)-1 downto 0) := (others => '0');

That's kind of overkill you'll never manage to count anything close to that even if you count packets for the next 100,000,000 years.

The reason you have no displayed value is the bit width is so large that changing it to unsigned causes what looks like a waveform display bug show up in Modelsim. If you leave it as hexadecimal the value shows up correctly as 1504'h000......000.
 

I don't understood your point. I need a counter so when circular buffer writes 188 bytes( a complete packet) then rd_en goes high and the read operation can happen and read that 188 bytes and then rd_en goes low till it sees another data_ready flag and read the next 188 bytes.

As you might seen in the previous message these are my issues :

1. I have a packet counter so it counts the data that is written to the circular buffer and when it reaches 188 bytes then the ready_data flag should go high. When I check the waveform , when counter reaches 1504 (=188*8) there is a clock cycle that counter has no value which i do not understand why ? So I believe because of that cycle on the next cycle of reading ( if read operation works !) I already missed one data which is E0 that is written to this address "5DF" (Please see the waveform in the attachment)

2. When data_ready flag goes high I need the read operation start reading the data while it keeps writing the rest of the data in the buffer but when the flag goes high, the read operation does not start. I checked the condition for reading operation and I can't see the issue.

3. In the testbench I have a single write at the beginning but then I override the value of that address before reading the value . How can I avoid overriding ? How can I say if there is already something in that address you can't write there ?
 

1. Your counter has 1504 bits, meaning it can count from 0 to 2^1504. COunting to 1504 only requires 11 bits, so the counter should be

Code:
signal packet_count : unsigned(10 downto 0) := (others => '0');

For 2 and 3 - this is debugging, and part of design. Follow the design backwards - what do you expect at the output. If its wrong, then work your way back through the design - does the next bit not work as expected, fix or work backwards again, until you hit the input.
 
1. I have a packet counter so it counts the data that is written to the circular buffer and when it reaches 188 bytes then the ready_data flag should go high. When I check the waveform , when counter reaches 1504 (=188*8) there is a clock cycle that counter has no value which i do not understand why ? So I believe because of that cycle on the next cycle of reading ( if read operation works !) I already missed one data which is E0 that is written to this address "5DF" (Please see the waveform in the attachment)

I already told you why....

Once again, The problem is with modelsim's display of UNSIGNED (i.e. radix -> unsigned in the right click menu on a signal name).

Here is the wave display with radix -> hexadecimal:
Capture1.PNG

here is the same display with radix -> unsigned:
Capture2.PNG

The orange signal above your packet_count signals is a combined signal with only packet_count(10 downto 0) to make it easier to see that changing the radix is only a problem for your definition of a 1504 bit wide packet_count. I'm pretty sure this is a Modelsim wave display bug.

I already missed one data which is E0 that is written to this address "5DF"
Looks like it got written to me...
Capture3.PNG

But it does look like you have a pipeline issue as that 0x5DF=0xE0 is instead getting written to 1505 (0x5E1)
 

I modified the design a little bit. I changed the counter so now the counter seems fine, but I still have the following issues :

1. When data_ready flag goes high I need the read operation start reading the data while it keeps writing the rest of the data in the buffer but when the flag goes high, the read operation does not start. I checked the condition for reading operation and I can't see the issue.

2. In the testbench I have a single write at the beginning but then I override the value of that address before reading the value . How can I avoid overriding ? How can I say if there is already something in that address you can't write there ?

I tried to separate write and read process in the design so it might solve my issue but now I have no data_out and when ready signal is high, read does not still start

Here is my modified code:


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity circular_buffer is
    generic(
        DATA_WIDTH : integer := 8;
        ADDR_WIDTH : integer := 12 -- 2^12 = 4096 = 4k
    );
    Port ( 
        -- Input clock domain
        clk         : in  STD_LOGIC;
        rstn        : in  STD_LOGIC;
        wr_en       : in  STD_LOGIC;
        wr_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_in     : in  STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
        
        -- Output clock domain
        s_clk       : in  STD_LOGIC;
        rd_en       : in  STD_LOGIC;
        rd_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_out    : out STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
    full        : out STD_LOGIC;
    empty       : out STD_LOGIC;
    error       : out STD_LOGIC;
        data_ready  : out STD_LOGIC
        
        );
end circular_buffer;
 
architecture Behavioral of circular_buffer is
 
constant ts_packet_size : integer := 188;
 
signal byte_count   : integer range 1 to DATA_WIDTH := 1;
signal packet_count : unsigned(10 downto 0) := (others => '0'); -- 2^11 = 2048 enough for 1504
signal fifo_full  : std_logic := '0';
signal fifo_empty : std_logic := '0';
signal ready      : std_logic := '0';
signal byte_flag  : std_logic := '0';
signal rd_ptr     : unsigned(ADDR_WIDTH-1 downto 0) := to_unsigned(0, ADDR_WIDTH);
signal wr_ptr     : unsigned(ADDR_WIDTH-1 downto 0) := to_unsigned(0, ADDR_WIDTH);
 
signal test_read  : std_logic := '0';
 
type memory_type is array (0 to 2**ADDR_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
 
signal memory : memory_type := (others => (others => '0'));
 
begin
 
full       <= fifo_full;
empty      <= fifo_empty;
data_ready <= ready;
 
 
error <= '1' when (fifo_empty='1' and rd_en='1') or (fifo_full='1' and wr_en='1') else '0';
 
process(clk, s_clk, rstn, wr_en)
begin
    if rstn = '0' then
        memory      <= (others => (others => '0'));
        rd_ptr      <= to_unsigned(0, ADDR_WIDTH);
        wr_ptr      <= to_unsigned(0, ADDR_WIDTH);
    byte_count  <= 1;
    packet_count<= (others => '0');
        fifo_full   <= '0';
        fifo_empty  <= '1';
    ready       <= '0';
        byte_flag   <= '0';
    data_out    <= (others => '0');
 
    test_read   <= '0';
 
    elsif rising_edge(clk) then
 
        -- Write
        if (wr_en = '1' and fifo_full = '0') then
            memory(to_integer(wr_ptr)) <= data_in;
            wr_ptr <= wr_ptr + 1;
    
        -- One packet is ready or not           
        if (packet_count = (8*ts_packet_size)) then
        ready <= '1';
        packet_count <= (others => '0');
        else
        ready <= '0';   
        packet_count <= packet_count + 1;   
            end if;
         end if;
 
        -- Memory full
        if (( wr_ptr + 1 = rd_ptr ) and (wr_en = '1') and (rd_en = '0')) then
            fifo_full <= '1';
    else
            fifo_full <= '0';
        end if;
 
        -- Memory empty
        if ((rd_ptr = wr_ptr) and (rd_en = '1') and (wr_en = '0')) then 
            fifo_empty <= '1';
 
    elsif ((rd_ptr /= wr_ptr) and (rd_en = '1')) then   -- if we read but write and read pointers do not point to the same location
        fifo_empty <= '0';
 
        elsif ((rd_en = '0') and (wr_en = '1') and (fifo_full = '0')) then
            fifo_empty <= '0';
 
    elsif rising_edge(wr_en) then
        fifo_empty <= '0';
    end if;
 
    end if;
        
end process;
 
 
read_process : process(s_clk)
begin
    if(rising_edge(s_clk)) then
        if(rstn = '1') then
    -- Read
    -- We read after having one complete transport stream packet
            if ( rd_en = '1' and fifo_empty = '0' and ready = '1') then
                report" ready to read" severity note;                       
                data_out  <= memory(to_integer(rd_ptr));
                        rd_ptr <= rd_ptr - 1;
            end if;
        end if;   
    end if;
end process;
 
end Behavioral;

 
Last edited by a moderator:

Now that makes sense you don't even use wr_addr to write to the memory you use wr_ptr, wr_addr isn't even used in the circular_buffer code.

Take Tricky's advice and start tracing back through the problem areas and see why they don't match what you think they should look like.

- - - Updated - - -

Stop using both code tags around syntax tags USE ONLY SYNTAX TAGS. I had to fix this again, so I can read the code easier.

Read the tutorial
 
I have tried to modify my design and testbench so I tried to divide my design into smaller portions and redesign it . what I have now is only a single write and single read without any empty or full condition but now the write and read pointers are X and the data out stays as XX. Would you please give me some suggestions about how to debug my design ?

Here is my small modified code:


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity circular_buffer is
    generic(
        DATA_WIDTH : integer := 8;
        ADDR_WIDTH : integer := 12 -- 2^12 = 4096 = 4k
    );
    Port ( 
        -- Input clock domain
        clk         : in  STD_LOGIC;
        rstn        : in  STD_LOGIC;
        wr_en       : in  STD_LOGIC;
        wr_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_in     : in  STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
 
        -- Output clock domain
        s_clk       : in  STD_LOGIC;
        rd_en       : in  STD_LOGIC;
        rd_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_out    : out STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
    full        : out STD_LOGIC;
    empty       : out STD_LOGIC;
    error       : out STD_LOGIC;
        data_ready  : out STD_LOGIC
        
        );
end circular_buffer;
 
architecture Behavioral of circular_buffer is
 
constant ts_packet_size : integer := 188;
 
signal byte_count   : integer range 1 to DATA_WIDTH := 1;
signal packet_count : unsigned(10 downto 0) := (others => '0'); -- 2^11 = 2048 enough for 1504
signal fifo_full  : std_logic := '0';
signal fifo_empty : std_logic := '0';
signal ready      : std_logic := '0';
signal byte_flag  : std_logic := '0';
signal rd_ptr     : unsigned(ADDR_WIDTH-1 downto 0) := to_unsigned(0, ADDR_WIDTH);
signal wr_ptr     : unsigned(ADDR_WIDTH-1 downto 0) := to_unsigned(0, ADDR_WIDTH);
 
signal test_read  : std_logic := '0';
 
type memory_type is array (0 to 2**ADDR_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
 
signal memory : memory_type := (others => (others => '0'));
 
begin
 
full       <= fifo_full;
empty      <= fifo_empty;
data_ready <= ready;
wr_ptr     <= unsigned(wr_addr);
rd_ptr     <= unsigned(rd_addr);
 
error <= '1' when (fifo_empty='1' and rd_en='1') or (fifo_full='1' and wr_en='1') else '0';
 
process(clk, s_clk, rstn, wr_en, wr_addr, data_in)
begin
    if rstn = '0' then
        memory      <= (others => (others => '0'));
        rd_ptr      <= to_unsigned(0, ADDR_WIDTH);
        wr_ptr      <= to_unsigned(0, ADDR_WIDTH);
    byte_count  <= 1;
    packet_count<= (others => '0');
        fifo_full   <= '0';
        fifo_empty  <= '1';
    ready       <= '0';
        byte_flag   <= '0';
    data_out    <= (others => '0');
 
    test_read   <= '0';
 
    elsif rising_edge(clk) then
 
        -- Write
        if (wr_en = '1' and fifo_full = '0') then
            memory(to_integer(wr_ptr)) <= data_in;
            wr_ptr <= wr_ptr + 1;
 
        -- One packet is ready or not           
        if (packet_count = (8*ts_packet_size)) then
        ready <= '1';
        packet_count <= (others => '0');
        else
        ready <= '0';   
        packet_count <= packet_count + 1;   
            end if;
         end if;
    end if;
        
end process;
 
 
read_process : process(s_clk, rd_addr, wr_en, rd_en, memory)
begin
    if(rising_edge(s_clk)) then
        if(rstn = '1') then
            if ( rd_en = '1') then                  
                data_out  <= memory(to_integer(rd_ptr));
                        rd_ptr <= rd_ptr - 1;
             end if;
         end if;   
     end if;
end process;
 
end Behavioral;



and here is my testbench:



Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
 
entity tb_circular_buffer is
end tb_circular_buffer;
 
architecture tb of tb_circular_buffer is
 
component circular_buffer
    generic(
        DATA_WIDTH  : integer := 8;
        ADDR_WIDTH  : integer := 12
    );
    port (
        clk        : in  std_logic;
        rstn       : in  std_logic;
        wr_en      : in  std_logic;
        wr_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_in    : in  std_logic_vector (DATA_WIDTH-1 downto 0);
    s_clk      : in std_logic;
        rd_en      : in  std_logic;
        rd_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_out   : out std_logic_vector (DATA_WIDTH-1 downto 0);
    full       : out std_logic;
        empty      : out std_logic;
    error      : out std_logic;
        data_ready : out std_logic
    );
end component;
 
constant DWIDTH   : integer := 8;
constant AWIDTH   : integer := 12;
 
signal clk        : std_logic;
signal rd_clk     : std_logic;
signal rstn       : std_logic;
signal wr_en      : std_logic;
signal wr_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_in    : std_logic_vector (DWIDTH-1 downto 0);
signal rd_en      : std_logic;
signal rd_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_out   : std_logic_vector (DWIDTH-1 downto 0);
signal full   : std_logic;
signal empty      : std_logic;
signal error      : std_logic;
signal data_ready : std_logic;
 
constant clk_period : time := 8 ns; --125MHz
 
-- Signal used to end simulator when we finished submitting our test cases
 
signal sim_done : boolean := false;
 
begin
    -- DUT instantiation
    dut : circular_buffer
    port map (clk        => clk,
              rstn       => rstn,
              wr_en      => wr_en,
              wr_addr    => wr_addr,
              data_in    => data_in,
          s_clk  => rd_clk,
              rd_en      => rd_en,
              rd_addr    => rd_addr,
              data_out   => data_out,
          full   => full,
          empty  => empty,
          error  => error,
              data_ready => data_ready);
 
    -- Clock generation
    write_clk_process : process
    begin
    if not sim_done then
            clk <= '1';
            wait for clk_period/2;
            clk <= '0';
            wait for clk_period/2;
    else
        wait;
    end if;
    end process;
 
 
    read_clk_process : process
    begin
    if not sim_done then
            rd_clk <= '1';
            wait for clk_period/2;
            rd_clk <= '0';
            wait for clk_period/2;
    else
        wait;
    end if;
    end process;
 
    stimuli : process
 
    -- Reset generation
    procedure async_rst is
    begin
        wait until rising_edge(clk);
        wait for clk_period;
        rstn <= '0';
        wait for clk_period;
        rstn <= '1';
    end procedure async_rst;
    
    begin
 
    -- intial state(default values)
        wr_en   <= '0';
        wr_addr <= (others => '0');
        data_in <= (others => '0');
        rd_en   <= '0';
    rstn <= '0';
        rd_addr <= (others => '0');
    wait for clk_period;
    async_rst;
 
        -- single write
    wait for 2*clk_period;
    wr_en   <= '1';
    wait until rising_edge(clk);
    data_in <= x"AB";
    wr_addr <= x"00A";
 
    wait until rising_edge(clk);
    data_in <= x"FA";
    wr_addr <= x"00B";
    wait until rising_edge(clk);
    wr_en   <= '0';
 
 
        -- single read (to check empty condition)
    rd_en   <= '1';
    wait until rising_edge(clk);
    rd_addr <= x"00A";
    wait until rising_edge(clk);
    
        rd_addr <= x"00B";
        wait until rising_edge(clk);
    rd_en   <= '0';
 
 
    sim_done <= true;
 
        wait;
    end process;
 
end tb;

 

Code:
[COLOR="#FF0000"][B]wr_ptr     <= unsigned(wr_addr);
rd_ptr     <= unsigned(rd_addr);[/B][/COLOR]
 
error <= '1' when (fifo_empty='1' and rd_en='1') or (fifo_full='1' and wr_en='1') else '0';
 
process(clk, s_clk, rstn, wr_en, wr_addr, data_in)
begin
    if rstn = '0' then
        memory      <= (others => (others => '0'));
[COLOR="#FF0000"][B]        rd_ptr      <= to_unsigned(0, ADDR_WIDTH);
        wr_ptr      <= to_unsigned(0, ADDR_WIDTH)[/B][/COLOR];

You should learn how to proofread your code. You are making two different assignments to the same signal in both a process and an assignment statement, hence you get Xs.

- - - Updated - - -

design_newbie said:
Hello,

I am not sure what are you talking about in the thread
HTML:
https://www.edaboard.com/showthread.php?384013-Circular-buffer-design
, I have an issue with the pointer. I don't know how to implement the following :

There is a memory with a range of addresses , then you have pointers which point to those addresses , I don't know how to relate the address and pointer in VHDL. Or if there is other ways to read from an address in memory.

What I had before in my design for write and read was as following :

Code:
begin
    if rstn = '0' then
        memory 	    <= (others => (others => '0'));
        rd_ptr 	    <= to_unsigned(0, ADDR_WIDTH);
        wr_ptr 	    <= to_unsigned(0, ADDR_WIDTH);
	byte_count  <= 1;
	packet_count<= (others => '0');
        fifo_full   <= '0';
        fifo_empty  <= '1';
	ready  	    <= '0';
        byte_flag   <= '0';
	data_out    <= (others => '0');

	test_read   <= '0';

    elsif rising_edge(clk) then

        -- Write
        if (wr_en = '1' and fifo_full = '0') then
           memory(to_integer(wr_ptr)) <= data_in;
	   wr_ptr 	   <= unsigned(wr_addr);
	   
            wr_ptr <= wr_ptr + 1;

	    -- One packet is ready or not			
	    if (packet_count = (8*ts_packet_size)) then
		ready <= '1';
		packet_count <= (others => '0');
	    else
		ready <= '0';   
		packet_count <= packet_count + 1;	
            end if;
         end if;
    end if;
        
end process;






read_process : process(s_clk, rd_addr, wr_en, rd_en, memory)
begin
	if(rising_edge(s_clk)) then
		if(rstn = '1') then
	                if ( rd_en = '1') then 				
				data_out  <= memory(to_integer(rd_ptr));
            			rd_ptr <= rd_ptr - 1;
			end if;
		end if;   
	end if;
end process;


but because I got the feedback to go through the design backward to find the issue , i tried to remove everything and just do a write and read which messed up even more with my design and I am so confused now. How can I relate my pointer to the address ? what is the best way to debug this ?

Would you please suggest me a solution or a path to follow ?

Thank you in advance
Do not directly contact users using primate messages to get answers.

- - - Updated - - -

You have two places in the code you posted where you are making assignments to the same signal, highlighed in red. This results in X's as the signals cannot have two drivers.

You should start with much simpler designs as your grasp of VHDL is to limited to understand what you are doing. Besides that you don't seem to have a good grasp of digital design, and your VHDL code reflects this. If you can't design the circuit in flip-flops and gates in a schematic drawing then you will not do a good job of writing VHDL that describes a hardware design.

When discussing tracing back signals you look at what generates signals and determine if they are high or low in the correct places in the simulation. If you find something that is incorrect then you trace back that input (to that logic) to determine why it is wrong. rinse and repeat.
 

Hello,

I modified the design but I am not able to figure out what causes the misbehavior in the design.

Here is the design :


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity circular_buffer is
    generic(
        DATA_WIDTH : integer := 8;
        ADDR_WIDTH : integer := 12 -- 2^12 = 4096 = 4k
    );
    Port ( 
        -- Input clock domain
        clk         : in  STD_LOGIC;
        rstn        : in  STD_LOGIC;
        wr_en       : in  STD_LOGIC;
        wr_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_in     : in  STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
        
        -- Output clock domain
        s_clk       : in  STD_LOGIC;
        rd_en       : in  STD_LOGIC;
        rd_addr     : in  STD_LOGIC_VECTOR (ADDR_WIDTH-1 downto 0);
        data_out    : out STD_LOGIC_VECTOR (DATA_WIDTH-1 downto 0);
    full        : out STD_LOGIC;
    empty       : out STD_LOGIC;
    error       : out STD_LOGIC;
        data_ready  : out STD_LOGIC        
        );
end circular_buffer;
 
architecture Behavioral of circular_buffer is
 
constant ts_packet_size : integer := 188;
 
signal byte_count   : integer range 1 to DATA_WIDTH := 1;
signal packet_count : unsigned(10 downto 0) := (others => '0'); -- 2^11 = 2048 enough for 1504
signal fifo_full    : std_logic := '0';
signal fifo_empty   : std_logic := '0';
signal ready        : std_logic := '0';
signal byte_flag    : std_logic := '0';
signal rd_ptr       : unsigned(ADDR_WIDTH-1 downto 0) := to_unsigned(0, ADDR_WIDTH);
signal wr_ptr       : unsigned(ADDR_WIDTH-1 downto 0) := to_unsigned(0, ADDR_WIDTH);
 
signal test_read  : std_logic := '0';
 
type memory_type is array (0 to 2**ADDR_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
 
signal memory : memory_type := (others => (others => '0'));
 
begin
 
full       <= fifo_full;
empty      <= fifo_empty;
data_ready <= ready;
 
error <= '1' when (fifo_empty='1' and rd_en='1') or (fifo_full='1' and wr_en='1') else '0';
 
process(clk, s_clk, rstn, wr_en, wr_addr, data_in, rd_en)
begin
    if rstn = '0' then
        rd_ptr      <= to_unsigned(0, ADDR_WIDTH);
        wr_ptr      <= to_unsigned(0, ADDR_WIDTH);
    byte_count  <= 1;
    packet_count<= (others => '0');
        fifo_full   <= '0';
        fifo_empty  <= '1';
    ready       <= '0';
        byte_flag   <= '0';
--  data_out    <= (others => '0');
 
    test_read   <= '0';
 
    elsif rising_edge(clk) then
 
        -- Write
        if (wr_en = '1' and fifo_full = '0') then
 
        memory(to_integer(unsigned(wr_addr))) <= data_in;      
            wr_ptr <= wr_ptr + 1;
 
        -- One packet is ready or not           
        if (packet_count = (8*ts_packet_size)) then
        ready <= '1';
        packet_count <= (others => '0');
        else
        ready <= '0';   
        packet_count <= packet_count + 1;   
            end if;
         end if;
    end if;
end process;
 
 
read_process : process(s_clk, rd_addr, wr_en, rd_en, memory)
begin
    if(rising_edge(s_clk)) then
        if(rstn = '1') then
                if ( rd_en = '1') then                  
                data_out <= memory(to_integer(unsigned(rd_addr)));
                        rd_ptr <= rd_ptr - 1;
            end if;
        end if;   
    end if;
end process;
 
end Behavioral;



and the testbench is :


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
 
entity tb_circular_buffer is
end tb_circular_buffer;
 
architecture tb of tb_circular_buffer is
 
component circular_buffer
    generic(
        DATA_WIDTH  : integer := 8;
        ADDR_WIDTH  : integer := 12
    );
    port (
        clk        : in  std_logic;
        rstn       : in  std_logic;
        wr_en      : in  std_logic;
        wr_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_in    : in  std_logic_vector (DATA_WIDTH-1 downto 0);
    s_clk      : in std_logic;
        rd_en      : in  std_logic;
        rd_addr    : in  std_logic_vector (ADDR_WIDTH-1 downto 0);
        data_out   : out std_logic_vector (DATA_WIDTH-1 downto 0);
    full       : out std_logic;
        empty      : out std_logic;
    error      : out std_logic;
        data_ready : out std_logic
    );
end component;
 
constant DWIDTH   : integer := 8;
constant AWIDTH   : integer := 12;
 
signal clk        : std_logic;
signal rd_clk     : std_logic;
signal rstn       : std_logic;
signal wr_en      : std_logic;
signal wr_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_in    : std_logic_vector (DWIDTH-1 downto 0);
signal rd_en      : std_logic;
signal rd_addr    : std_logic_vector (AWIDTH-1 downto 0);
signal data_out   : std_logic_vector (DWIDTH-1 downto 0);
signal full   : std_logic;
signal empty      : std_logic;
signal error      : std_logic;
signal data_ready : std_logic;
 
constant clk_period : time := 8 ns; --125MHz
 
-- Signal used to end simulator when we finished submitting our test cases
signal sim_done : boolean := false;
 
begin
    -- DUT instantiation
    dut : circular_buffer
    port map (clk        => clk,
              rstn       => rstn,
              wr_en      => wr_en,
              wr_addr    => wr_addr,
              data_in    => data_in,
          s_clk  => rd_clk,
              rd_en      => rd_en,
              rd_addr    => rd_addr,
              data_out   => data_out,
          full   => full,
          empty  => empty,
          error  => error,
              data_ready => data_ready);
 
    -- Clock generation
    write_clk_process : process
    begin
    if not sim_done then
            clk <= '1';
            wait for clk_period/2;
            clk <= '0';
            wait for clk_period/2;
    else
        wait;
    end if;
    end process;
 
 
    read_clk_process : process
    begin
    if not sim_done then
            rd_clk <= '1';
            wait for clk_period/2;
            rd_clk <= '0';
            wait for clk_period/2;
    else
        wait;
    end if;
    end process;
 
    stimuli : process
 
    -- Reset generation
    procedure async_rst is
    begin
        wait until rising_edge(clk);
        wait for clk_period;
        rstn <= '0';
        wait for clk_period;
        rstn <= '1';
    end procedure async_rst;
    
    begin
 
    -- intial state(default values)
        wr_en   <= '0';
        wr_addr <= (others => '0');
        data_in <= (others => '0');
        rd_en   <= '0';
    rd_addr <= (others => '0');
    rstn <= '0';
    wait for clk_period;
    async_rst;
 
        -- single write
    wait for 2*clk_period;
    wr_en   <= '1';
    wait until rising_edge(clk);
    data_in <= x"AB";
    wr_addr <= x"00A";
    wait until rising_edge(clk);
    wr_en   <= '0';
 
    wait until rising_edge(clk);
 
        -- single read (to check empty condition)
    rd_en   <= '1';
    rd_addr <= x"00A";
    assert data_out = x"AB" report "data out is not correct" severity warning;
    wait until rising_edge(clk);
    rd_en   <= '0';
 
    -- write to all locations
    for i in 1 to 4096 loop
        wr_en   <= '1';
        data_in <= conv_std_logic_vector(i,8);
        wr_addr <= conv_std_logic_vector(i-1,12);
        wait until rising_edge(clk);
            wr_en   <= '0';
    end loop;
 
    -- Instruct "clk_process" to halt execution.
    sim_done <= true;
 
        wait;
    end process;
 
end tb;



Misbehavior :

1. When I do a single write in the test then I do a read from the same address the expected result is that when when rd_en goes high, we read the value located in the address "00A" which is"AB" and put it in data out and the rd_ptr moves to "4095". What I observe in the waveform is when I read from address "00A" , data out is "XX" and also read pointer is not decremented and it changes from "0" to "X".

2. When we write to all locations , we get to the same address as before which is "00A" but I need a mechanism that if that memory space have a data in it do not override it which does not happen and it simply override "AB" with the new value which is "11". So when we read from the same location (issue No.1) imagine that the read happens correctly , that address in the memory should be empty after read but it does not.

Another issue that I have is the concept of pointers and if they will work in the same way in VHDL or even if it is necessary to use pointers or not or I just can read or write to a memory address using :


Code VHDL - [expand]
1
2
3
data_out <= memory(to_integer(unsigned(rd_addr)));
 
memory(to_integer(unsigned(wr_addr))) <= data_in;



Waveform:

circular buffer - overriden , read pointer , data out.JPG

Would you please explain to me in details or if you have a suggestion be more precious, Thank you .
 
Last edited:

Why don't you just read this paper on designing asynchronous FIFOs. It might help you.

Not sure why you have a pointer and an address, RAMs use addresses to access the memory array. This is hardware you have a RAM it has basically the following ports (look at your techonology's library primitives): address, data input, data output, read enable, write enable, and (maybe) byte enables.

I also don't get why you are multiplying the ts_packet_size by 8 and have a comment above that says "one packet is ready or not". An MPEG TS packet is 188-bytes it's not 1504 bytes, you are receiving the data as bytes, so multiplying by 8 only makes sense if you are processing those bytes as as a serial stream (which you aren't doing).

I'm not sure I can help you further, I don't have the time to teach you digital design or debugging. Perhaps you could take a digital design class at your local university.
 
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top