Skip to content
Snippets Groups Projects
Select Git revision
  • a2bc302368df6bb5d31828a8d78f93637d6f8f04
  • master default protected
  • peters-master-patch-97522
  • peters-master-patch-20805
  • v23.0
  • v22.0
6 results

tutorial.rst

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    io_ddr_driver.vhd 10.77 KiB
    --------------------------------------------------------------------------------
    --
    -- Copyright (C) 2014
    -- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
    -- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
    -- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    --
    --------------------------------------------------------------------------------
    
    -- Purpose: Provide streaming interface to DDR memory
    -- Description:
    --   Write or read a block of data to or from DDR memory. The data width is set
    --   by the DDR controller data width given by RESIZE_MEM_CTLR_DATA() and eg.
    --   256 bits for DDR3 with 64 bit DQ data. The block of data is located from
    --   dvr_start_address to dvr_nof_data.
    --   The io_ddr_driver takes care that the access is done in a number of bursts.
    --   The burst size for both write and read depends on the maximum burst size
    --   and the remaining block size.
    -- Remarks:
    -- . Both this driver and the DDR IP controller use the t_mem_ctlr_miso/mosi
    --   interface. The maximum burst size of the controller is defined by 
    --   g_tech_ddr.maxburstsize and eg. 64 ctlr data words. The maximum burst size
    --   of this driver is as large as the entire ctlr address span. The burst size
    --   of driver depends on the block size of the application.
    
    
    LIBRARY IEEE, tech_ddr_lib, common_lib, dp_lib;
    USE IEEE.STD_LOGIC_1164.ALL;
    USE IEEE.NUMERIC_STD.ALL;
    USE common_lib.common_pkg.ALL;
    USE common_lib.common_mem_pkg.ALL;
    USE dp_lib.dp_stream_pkg.ALL;
    USE tech_ddr_lib.tech_ddr_pkg.ALL;
    
    ENTITY io_ddr_driver IS 
      GENERIC (
        g_tech_ddr         : t_c_tech_ddr
      );
      PORT ( 
        rst                : IN  STD_LOGIC;
        clk                : IN  STD_LOGIC;
    
        dvr_miso           : OUT t_mem_ctlr_miso;
        dvr_mosi           : IN  t_mem_ctlr_mosi;
       
        wr_snk_in          : IN  t_dp_sosi;
        wr_snk_out         : OUT t_dp_siso;
        
        rd_src_out         : OUT t_dp_sosi;
        rd_src_in          : IN  t_dp_siso;
        
        ctlr_miso          : IN  t_mem_ctlr_miso;
        ctlr_mosi          : OUT t_mem_ctlr_mosi
       );
    END io_ddr_driver;
    
    
    ARCHITECTURE str OF io_ddr_driver IS
    
      CONSTANT c_ctlr_address_w     : NATURAL := func_tech_ddr_ctlr_address_w(g_tech_ddr);
     
      TYPE t_state_enum IS (s_init, s_idle, s_wait, s_rd_request, s_wr_request, s_wr_burst);
    
      SIGNAL state                  : t_state_enum;
      SIGNAL nxt_state              : t_state_enum; 
      SIGNAL prev_state             : t_state_enum; 
    
      SIGNAL dvr_en                 : STD_LOGIC;
      SIGNAL dvr_wr_not_rd          : STD_LOGIC;       
      SIGNAL dvr_start_address      : STD_LOGIC_VECTOR(c_ctlr_address_w-1 DOWNTO 0);
      SIGNAL dvr_nof_data           : STD_LOGIC_VECTOR(c_ctlr_address_w-1 DOWNTO 0);
      SIGNAL dvr_done               : STD_LOGIC;
      SIGNAL nxt_dvr_done           : STD_LOGIC;
    
      SIGNAL burst_size             : POSITIVE RANGE 1 TO 2**g_tech_ddr.maxburstsize_w-1;      -- burst size >= 1
      SIGNAL nxt_burst_size         : POSITIVE;
      SIGNAL burst_wr_cnt           : STD_LOGIC_VECTOR(g_tech_ddr.maxburstsize_w-1 DOWNTO 0);  -- count down from burst_size to 0
      SIGNAL nxt_burst_wr_cnt       : STD_LOGIC_VECTOR(g_tech_ddr.maxburstsize_w-1 DOWNTO 0);
    
      SIGNAL cur_address            : STD_LOGIC_VECTOR(c_ctlr_address_w-1 DOWNTO 0);
      SIGNAL nxt_cur_address        : STD_LOGIC_VECTOR(c_ctlr_address_w-1 DOWNTO 0);   
      SIGNAL address_cnt            : STD_LOGIC_VECTOR(c_ctlr_address_w-1 DOWNTO 0);  -- count down nof addresses = nof ctlr data words
      SIGNAL nxt_address_cnt        : STD_LOGIC_VECTOR(c_ctlr_address_w-1 DOWNTO 0);   
    
    BEGIN
    
      -- Map original dvr interface signals to t_mem_ctlr_mosi/miso
      dvr_miso.done     <= dvr_done;             -- Requested wr or rd sequence is done
      dvr_en            <= dvr_mosi.burstbegin;
      dvr_wr_not_rd     <= dvr_mosi.wr;          -- No need to use dvr_mosi.rd
      dvr_start_address <= dvr_mosi.address(c_ctlr_address_w-1 DOWNTO 0);
      dvr_nof_data      <= dvr_mosi.burstsize(c_ctlr_address_w-1 DOWNTO 0);
    
      p_clk : PROCESS(rst, clk)
      BEGIN
        IF rst='1' THEN
          state                <= s_init;
          burst_wr_cnt         <= (OTHERS=>'0');
          dvr_done             <= '0';
          cur_address          <= (OTHERS=>'0');
          address_cnt          <= (OTHERS=>'0');
          burst_size           <= 1;
          prev_state           <= s_idle;
        ELSIF rising_edge(clk) THEN
          state                <= nxt_state;
          burst_wr_cnt         <= nxt_burst_wr_cnt;
          dvr_done             <= nxt_dvr_done;
          cur_address          <= nxt_cur_address;
          address_cnt          <= nxt_address_cnt;
          burst_size           <= nxt_burst_size;
          prev_state           <= state;
        END IF;
      END PROCESS;
    
      p_burst_size : PROCESS (address_cnt)
      BEGIN 
        -- Access burst size is at least 1 and if more then set to the smallest of g_tech_ddr.maxburstsize and address_cnt
        nxt_burst_size <= 1;
        IF UNSIGNED(address_cnt) > 0 THEN
          nxt_burst_size <= g_tech_ddr.maxburstsize;
          IF UNSIGNED(address_cnt) < g_tech_ddr.maxburstsize THEN
            nxt_burst_size <= TO_UINT(address_cnt);
          END IF;
        END IF;
      END PROCESS;
      
      rd_src_out.valid <= ctlr_miso.rdval;
      rd_src_out.data <= RESIZE_DP_DATA(ctlr_miso.rddata);
      
      p_state : PROCESS(prev_state, state,
                        dvr_en, dvr_wr_not_rd, dvr_start_address, dvr_nof_data,
                        ctlr_miso, wr_snk_in, rd_src_in, 
                        burst_size, burst_wr_cnt, cur_address, address_cnt)
      BEGIN  
        nxt_state              <= state;
        
        ctlr_mosi.address      <= RESIZE_MEM_CTLR_ADDRESS(cur_address);
        ctlr_mosi.wrdata       <= RESIZE_MEM_CTLR_DATA(wr_snk_in.data);
        ctlr_mosi.wr           <= '0';
        ctlr_mosi.rd           <= '0';
        ctlr_mosi.burstbegin   <= '0'; 
        ctlr_mosi.burstsize    <= (OTHERS => '0');
        
        wr_snk_out.ready       <= '0';
        nxt_burst_wr_cnt       <= burst_wr_cnt;    
        nxt_dvr_done           <= '0';
        nxt_cur_address        <= cur_address;
        nxt_address_cnt        <= address_cnt;
    
        CASE state IS
         
          WHEN s_wr_burst => -- Performs the burst portion (word 2+)        
            IF ctlr_miso.waitrequest_n = '1' THEN
              IF wr_snk_in.valid = '1' THEN         -- it is allowed that valid is not always active during a burst
                wr_snk_out.ready <= '1';            -- wr side uses latency of 0, so wr_snk_out.ready<='1' acknowledges a successful write request.
                ctlr_mosi.wr     <= '1';
                nxt_burst_wr_cnt <= INCR_UVEC(burst_wr_cnt, -1);
                IF UNSIGNED(burst_wr_cnt) = 1 THEN  -- check for the last cycle of this burst sequence
                  nxt_state <= s_wr_request;        -- initiate a new wr burst or goto idle via the wr_request state
                END IF;
              END IF;
            END IF;            
    
          WHEN s_wr_request =>  -- Performs 1 write access and goes into s_wr_burst when requested write words >1       
            IF UNSIGNED(address_cnt) = 0 THEN -- end address reached
              nxt_dvr_done  <= '1';              
              nxt_state     <= s_idle;          
            ELSIF ctlr_miso.waitrequest_n = '1' THEN 
              IF wr_snk_in.valid = '1' THEN
                -- Always perform 1st write here             
                wr_snk_out.ready     <= '1';
                ctlr_mosi.wr         <= '1';
                ctlr_mosi.burstbegin <= '1';                                -- assert burstbegin,
                ctlr_mosi.burstsize  <= TO_MEM_CTLR_BURSTSIZE(burst_size);  -- burstsize >= 1
                nxt_cur_address      <= INCR_UVEC(cur_address, burst_size);
                nxt_address_cnt      <= INCR_UVEC(address_cnt, -burst_size);
                nxt_burst_wr_cnt     <= TO_UVEC(burst_size-1, g_tech_ddr.maxburstsize_w);
                -- Return for next wr request or perform any remaining writes in this burst
                nxt_state <= s_wait; 
                IF burst_size > 1 THEN
                  nxt_state <= s_wr_burst;  -- first burst wr cycle is done here, the rest are done in s_wr_burst
                END IF;
              END IF;
            END IF;        
    
          WHEN s_rd_request =>  -- Posts a read request for a burst (1...g_tech_ddr.maxburstsize)      
            IF UNSIGNED(address_cnt) = 0 THEN -- end address reached
              nxt_dvr_done  <= '1';              
              nxt_state     <= s_idle;
            ELSE 
              IF rd_src_in.ready = '1' THEN  -- the external FIFO uses almost full level assert its snk_out.ready and can then still accept the maximum rd burst of words
                IF ctlr_miso.waitrequest_n = '1' THEN    
                  ctlr_mosi.rd         <= '1';                   
                  ctlr_mosi.burstbegin <= '1';                                -- assert burstbegin,
                  ctlr_mosi.burstsize  <= TO_MEM_CTLR_BURSTSIZE(burst_size);  -- burstsize >= 1
                  nxt_cur_address      <= INCR_UVEC(cur_address, burst_size);  
                  nxt_address_cnt      <= INCR_UVEC(address_cnt, -burst_size);
                  -- Return for next rd request
                  nxt_state <= s_wait;
                END IF;           
              END IF;
            END IF; 
    
          -- In this state address_cnt is valid and in the next state burst_size (that depends on address_cnt) will be valid.
          -- Therefore this wait state is inserted between any requests.
          WHEN s_wait =>
            IF prev_state = s_wr_request THEN nxt_state <= s_wr_request; END IF;  -- between wr-wr burst requests
            IF prev_state = s_rd_request THEN nxt_state <= s_rd_request; END IF;  -- between rd-rd burst requests
            IF prev_state = s_idle THEN                                           -- between wr and rd accesses
              IF dvr_wr_not_rd = '1' THEN
                nxt_state <= s_wr_request;
              ELSE
                nxt_state <= s_rd_request;
              END IF;
            END IF;
             
          WHEN s_idle =>
            nxt_dvr_done <= '1';    -- assert dvr_done after s_init or keep it asserted after a finished access
            IF dvr_en = '1' THEN  
              nxt_cur_address <= dvr_start_address; 
              nxt_address_cnt <= dvr_nof_data;
              nxt_dvr_done    <= '0';
              nxt_state       <= s_wait;
            END IF;
         
          WHEN OTHERS => -- s_init
            nxt_dvr_done <= '0';
            IF ctlr_miso.done = '1' THEN
              nxt_state <= s_idle;  -- and assert dvr_done when in s_idle to indicate ctlr_miso.done
            END IF;
          
        END CASE;
      END PROCESS;
    
    END str;