MCB Frame Buffer

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project was completed in April 2013.

Mcb test pattern.jpg

It is by no means completed, but is a fully working DDR RAM based frame buffer, using the Spartan 6 MCB interface. If you make use of this source please send me an email - I'ld love to know what you use it for!

Contents

Source files

test_pattern_writer.vhd

This file has been updated from the screenshot above to include a couple of white frames and a diagonal white line to check byte ordering and that all pixels are visible.

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
-- 
-- Description: Writes a simple test pattern into a MCB connected RAM
-- 
-- Writes a byte at time over a 32 bit interface.
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity Test_pattern_writer is
   Generic (
      hVisible : natural;
      vVisible : natural
   );
   Port ( clk : in  STD_LOGIC;
      memory_ready      : IN std_logic;
      completed         : OUT std_logic;
      write_cmd_enable  : OUT std_logic;
      write_cmd_address : OUT std_logic_vector(29 downto 0);
      write_cmd_empty   : IN  std_logic;
      write_cmd_full    : IN  std_logic;
      
      write_data_empty  : IN  std_logic;
      write_data_count  : IN  std_logic_vector(6 downto 0); -- How many words are queued 
      write_data_enable : OUT std_logic;
      write_mask        : OUT std_logic_vector(3 downto 0);
      write_data        : OUT std_logic_vector(31 downto 0)
    );
end Test_pattern_writer;

architecture Behavioral of Test_pattern_writer is   
   signal x       : unsigned (10 downto 0) := (others => '0');
   signal y       : unsigned (10 downto 0) := (others => '0');
   signal address : unsigned (21 downto 0) := (others => '0');
   
   signal colour                : std_logic_vector(7 downto 0);
   signal pending_write         : std_logic := '0';
   signal start_write           : std_logic := '0';
   signal pending_write_address : std_logic_vector (21 downto 0) := (others => '0');
   signal completed_reg         : std_logic := '0';
begin
   colour <= std_logic_vector(x(8 downto 6) & y(8 downto 6) & (x(5 downto 4)+y(5 downto 4)));
   completed <= '1' when y = to_unsigned(vVisible,11) else '0';
   
process(clk)
   begin
      if rising_edge(clk) then
         write_cmd_address <= "00000000" & pending_write_address;
         write_cmd_enable <= pending_write;

         -- a white outline, with colour blocks			
         if x = 0 or y = 0 or x = hVisible -1 or y = vVisible - 1 then 
            write_data <= x"FFFFFFFF";
         elsif x = 100 or y = 100 or x = hVisible -101 or y = vVisible - 101 then 
            write_data <= x"FFFFFFFF";
         else
             write_data <= colour & colour & colour  & colour;
         end if;
      
         if start_write = '1' then
            pending_write <= '1';
            pending_write_address <= std_logic_vector(address(21 downto 2) & "00");
            write_data_enable <= '1';
            case address(1 downto 0) is
               when "00"   => write_mask <= "1110";
               when "01"   => write_mask <= "1101";
               when "10"   => write_mask <= "1011";
               when others => write_mask <= "0111";
            end case;
            
            address <= address + 1;
            if x = to_unsigned(hVisible-1,11) then
               x <= (others => '0');
               y <= y + 1;
            else
               x <= x + 1;
            end if;               
         else
            write_data_enable <= '0';
            pending_write <= '0';
         end if;

         start_write <=  '0';
         if write_data_count(6) = '0' and write_cmd_empty = '1' and write_data_empty = '1' then
            -- Do we need to actually write anything?
            if y /= to_unsigned(vVisible,11)  then
               start_write <= memory_ready;
            end if;
         end if;
      end if;
   end process;
end Behavioral;

mcb_vga.vhd

This is where all the business happens!

On the line before the first visible frame four read requests are issued, each for 16 words (64 bytes). This fills the data FIFO with the first 256 pixels. On the all visible lines, except the final visible line, a read command is issued every 64 visible pixels. On the last visible line the last four read commands are not issued (as we do not need that data).

This is not a perfect solution. If a read takes longer than 192 cycles then the data FIFO can be exhausted and random pixels may be inserted in the display. To minimise issues that may occur should the FIFOs get unsynchronised the FIFO is drained on the first line of the vertical blanking interval One solution for this could be to insert a larger FIFO between the MCB and the display. Another solution could be to issue a refresh command to the MCB at the end of each visible line, ensuring that the MCB does not have to insert hidden refresh cycles.

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hasmter@snap.net.nz>
-- 
-- Module Name: mcb_vga.vhd - Behavioral 
--
-- Description: Reads from the MCB to display a picture on the screen.
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity mcb_vga is
	GENERIC (
		-- Timings for 1280x720@60Hx
		hVisible    : natural;
		hSyncStart  : natural;
		hSyncEnd    : natural;
		hMax        : natural;
		hSyncActive : std_logic;

		vVisible    : natural;
		vSyncStart  : natural;
		vSyncEnd    : natural;
		vMax        : natural;
		vSyncActive : std_logic
	);
    Port ( clk_reader : in  STD_LOGIC;
           hsync      : out  STD_LOGIC;
           vsync      : out  STD_LOGIC;
           red        : out  STD_LOGIC_VECTOR (2 downto 0);
           green      : out  STD_LOGIC_VECTOR (2 downto 0);
           blue       : out  STD_LOGIC_VECTOR (2 downto 1);
           
           memory_ready : in  std_logic;

            -- Reads are in a burst length of 16
            read_cmd_enable   : out std_logic;
            read_cmd_refresh  : out  std_logic;
            read_cmd_address  : out std_logic_vector(29 downto 0);
            read_cmd_full     : in  std_logic;
            read_cmd_empty    : in  std_logic;
            --
            read_data_enable  : out std_logic;
            read_data         : in  std_logic_vector(31 downto 0);
            read_data_empty   : in  std_logic;
            read_data_full    : in  std_logic;
            read_data_count   : in  std_logic_vector(6 downto 0)
   );
end mcb_vga;

architecture Behavioral of mcb_vga is
   signal hCounter : unsigned(10 downto 0) := (others => '0');
   signal vCounter : unsigned(10 downto 0) := (others => '0');
   signal address  : unsigned(29 downto 0) := (others => '0');
   signal read_cmd_enable_local : std_logic := '0';
begin
   read_cmd_address <= std_logic_vector(address);
   read_cmd_enable  <= read_cmd_enable_local;

process(clk_reader)
   begin
      if rising_edge(clk_reader) then
         if read_cmd_enable_local = '1' then
            address <= address + 64;  -- Address is the byte address, so each read is 16 words
         end if;

         -------------------------------------------
         -- should we issue a read command?
         -------------------------------------------
         read_cmd_enable_local <= '0';
         if hCounter >= hVisible-64 then
            read_cmd_refresh <= '1';
         else
            read_cmd_refresh <= '0';
         end if;
         if hCounter(5 downto 0) = "111100" then -- once out of 64 cycles
            if vCounter < vVisible-1 then
               if hCounter < hVisible then 
                  -- issue a read every 64th cycle of a visible line (except last)
                  read_cmd_enable_local <= memory_ready and not read_cmd_full;
               end if;
            elsif vCounter = vVisible-1 then
               -- don't issue the last three reads on the last line
               if hCounter < (hVisible-4*64) then 
                  read_cmd_enable_local <= memory_ready and not read_cmd_full;
               end if;
            elsif vCounter = vMax-1 then 
               -- prime the read queue just before the first line with 3 read * 16 words * 4 bytes = 192 bytes
               if hCounter < 4 * 64 then
                  read_cmd_enable_local <= memory_ready and not read_cmd_full;            
               end if;
            end if;   
         end if;
         
         -------------------------------------------
         -- Should we read a word from the read FIFO
         -------------------------------------------
         read_data_enable <= '0';

         -------------------------------------------
         -- Flushing the MCB's read port at the end of frame
         -------------------------------------------
         if vCounter = vVisible then
         --   read_data_enable <= memory_ready and not read_data_empty;
            address <= (others => '0');
         end if;

         -------------------------------------------
         -- Display pixels and trigger data FIFO reads
         -------------------------------------------
         if hCounter < hVisible and vCounter < vVisible then 
            case hcounter(1 downto 0) is
               when "00" =>
                  red   <= read_data( 7 downto 5);
                  green <= read_data( 4 downto 2);
                  blue  <= read_data( 1 downto 0);
               when "01" =>
                  red   <= read_data(15 downto 13);
                  green <= read_data(12 downto 10);
                  blue  <= read_data( 9 downto  8);
               when "10" =>
                  red   <= read_data(23 downto 21);
                  green <= read_data(20 downto 18);
                  blue  <= read_data(17 downto 16);
                  -- read_data_enable will be asserted next cycle,
                  -- so read_data will change the one following that
                  read_data_enable <= memory_ready and not read_data_empty;
               when others =>
                  red   <= read_data(31 downto 29);
                  green <= read_data(28 downto 26);
                  blue  <= read_data(25 downto 24);
            end case; 
         else
            red   <= (others => '0');
            green <= (others => '0');
            blue  <= (others => '0');
         end if;

         -------------------------------------------
         -- track the horizontal and vertical position
         -- and generate sync pulses
         -------------------------------------------
         if hCounter = hMax then
            hCounter <= (others => '0');
            if vCounter = vMax then 
               vCounter <= (others => '0');
            else
               vCounter <= vCounter +1;
            end if;
            
            if vCounter = vSyncStart then
               vSync <= vSyncActive;
            end if;
         
            if vCounter = vSyncEnd then
               vSync <= not vSyncActive;
            end if;
         else
            hCounter <= hCounter+1;
         end if;
         
         if hCounter = hSyncStart then
            hSync <= hSyncActive;
         end if;
         
         if hCounter = hSyncEnd then
            hSync <= not hSyncActive;
         end if;         
      end if;
   end process;
end Behavioral;

MIG generated sources

These source files were initially generated by the Xilinx Memory Interface Generator (MIG) tool. As such I don't have any ownership over them. This zip file contains screen shots of the process to generate the memory controller:

File:MIG screenshots.zip

Once generated, you can find the files you need. If you want you can remove the mem32 component from your project, and then add all of the following VHD files to your project, allowing your to edit them in the IDE:

Mcb vhd files.jpg

And then you also need to have the "mcb.ucf" file to you project to define the pin mappings:

Mcb ucf files.jpg

After generation I've edited the files to expose the calibration clock so I can use it as the VGA Pixel clock.

Here are the changes I've made. Add the following signal to the mem32 entity's ports.

  mcb_drp_clk                             : out std_logic;

In the architecture add the following to pass the calibration clock out of the memory controller:

  mcb_drp_clk   <= c3_mcb_drp_clk;

Check the values for the following constants (also in mem32.vhd):

  constant C3_CLKOUT0_DIVIDE       : integer := 6; 
  constant C3_CLKOUT1_DIVIDE       : integer := 6; 
  constant C3_CLKOUT2_DIVIDE       : integer := 18; 
  constant C3_CLKOUT3_DIVIDE       : integer := 8; 
  constant C3_CLKFBOUT_MULT        : integer := 12; 
  constant C3_DIVCLK_DIVIDE        : integer := 1; 

The default clock frequencies in design are:

  • The PLL frequency will run at (InputFrequency * C3_CLKFBOUT_MULT)/C3_DIVCLK_DIVIDE = 50MHz * 12 / 1 = 600MHz.
  • The memory clocks (CLKOUT0 & CLKOUT1) will be 600MHz/6 = 100Mhz
  • The clk0 output (CLKOUT2) will be 600MHz/18 = 33.3MHz
  • The mcb_drp_clk (CLKOUT3) will be 600MHz/8 = 75MHz

You can tweak the memory clock frequencies (and throughput) by changing both C3_CLKOUT0_DIVIDE and C3_CLKOUT1_DIVIDE. Setting both to '4' will run the memory at 150MHz (600MB/s).

If you need to add additional clocks, then you will need to modify memc3_infrastructure.vhd as well to generate the additional frequencies and pass the signal out to your design.

mem_wrapper.vhd

This module is used to hide the detail of the MCB's interface. It makes Port0 into a read-only port that only performs 16 word reads, and Port1 into a single word write-only port.

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hasmter@snap.net.nz>
-- 
-- Module Name: mem_wrapper.vhd - Behavioral 
--
-- Description: Creates a tidy interface into the MCB
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity mem_wrapper is
    Port ( 
      clk_sys     : in std_logic;
      clk_sys_out : out std_logic;
      clk_calibration : out std_logic;
      clk_writer  : in std_logic;
      clk_reader  : in std_logic;

      -- Writes are in a burst length of 1
      write_cmd_enable  : in  std_logic;
      write_cmd_empty   : out std_logic;
      write_cmd_full    : out std_logic;
      write_cmd_address : in  std_logic_vector(29 downto 0);
      --
      write_data_enable : in  std_logic;
      write_mask        : in  std_logic_vector(3 downto 0);
      write_data        : in  std_logic_vector(31 downto 0);      
      write_data_empty  : out std_logic;
      write_data_full   : out std_logic;
      write_data_count  : out std_logic_vector(6 downto 0);

      -- Reads are in a burst length of 16
      read_cmd_enable   : in  std_logic;
      read_cmd_address  : in  std_logic_vector(29 downto 0);
      read_cmd_full     : out std_logic;
      read_cmd_empty    : out std_logic;
      --
      read_data_enable  : in  std_logic;
      read_data         : out std_logic_vector(31 downto 0);
      read_data_empty   : out std_logic;
      read_data_full   : out std_logic;
      read_data_count   : out std_logic_vector(6 downto 0);


      mcb3_dram_dq    : inout std_logic_vector(15 downto 0);
      mcb3_dram_a     : out   std_logic_vector(12 downto 0);
      mcb3_dram_ba    : out   std_logic_vector( 1 downto 0);
      mcb3_dram_cke   : out   std_logic;
      mcb3_dram_ras_n : out   std_logic;
      mcb3_dram_cas_n : out   std_logic;
      mcb3_dram_we_n  : out   std_logic;
      mcb3_dram_dm    : out   std_logic;
      mcb3_dram_udqs  : inout std_logic;
      mcb3_rzq        : inout std_logic;
      mcb3_dram_udm   : out   std_logic;
      mcb3_dram_dqs   : inout std_logic;
      mcb3_dram_ck    : out   std_logic;
      mcb3_dram_ck_n  : out   std_logic;
      -- status
      calib_done      : out   std_logic;
      read_error      : out   std_logic;
      read_overflow   : out   std_logic;
      write_error     : out   std_logic;
      -- reset         
      reset           : in    std_logic
    );
end mem_wrapper;

architecture Behavioral of mem_wrapper is
component mem32
 generic(
    C3_P0_MASK_SIZE           : integer := 4;
    C3_P0_DATA_PORT_SIZE      : integer := 32;
    C3_P1_MASK_SIZE           : integer := 4;
    C3_P1_DATA_PORT_SIZE      : integer := 32;
    C3_MEMCLK_PERIOD          : integer := 20000;
    C3_RST_ACT_LOW            : integer := 0;
    C3_INPUT_CLK_TYPE         : string := "SINGLE_ENDED";
    C3_CALIB_SOFT_IP          : string := "TRUE";
    C3_SIMULATION             : string := "FALSE";
    DEBUG_EN                  : integer := 1;
    C3_MEM_ADDR_ORDER         : string := "ROW_BANK_COLUMN";
    C3_NUM_DQ_PINS            : integer := 16;
    C3_MEM_ADDR_WIDTH         : integer := 13;
    C3_MEM_BANKADDR_WIDTH     : integer := 2
);
    port (
   mcb3_dram_dq                            : inout  std_logic_vector(C3_NUM_DQ_PINS-1 downto 0);
   mcb3_dram_a                             : out std_logic_vector(C3_MEM_ADDR_WIDTH-1 downto 0);
   mcb3_dram_ba                            : out std_logic_vector(C3_MEM_BANKADDR_WIDTH-1 downto 0);
   mcb3_dram_cke                           : out std_logic;
   mcb3_dram_ras_n                         : out std_logic;
   mcb3_dram_cas_n                         : out std_logic;
   mcb3_dram_we_n                          : out std_logic;
   mcb3_dram_dm                            : out std_logic;
   mcb3_dram_udqs                          : inout  std_logic;
   mcb3_rzq                                : inout  std_logic;
   mcb3_dram_udm                           : out std_logic;
   c3_sys_clk                              : in  std_logic;
   c3_sys_rst_i                            : in  std_logic;
   c3_calib_done                           : out std_logic;
   c3_clk0                                 : out std_logic;
   c3_rst0                                 : out std_logic;
   mcb_drp_clk                               : out std_logic;
   mcb3_dram_dqs                           : inout  std_logic;
   mcb3_dram_ck                            : out std_logic;
   mcb3_dram_ck_n                          : out std_logic;
   c3_p0_cmd_clk                           : in std_logic;
   c3_p0_cmd_en                            : in std_logic;
   c3_p0_cmd_instr                         : in std_logic_vector(2 downto 0);
   c3_p0_cmd_bl                            : in std_logic_vector(5 downto 0);
   c3_p0_cmd_byte_addr                     : in std_logic_vector(29 downto 0);
   c3_p0_cmd_empty                         : out std_logic;
   c3_p0_cmd_full                          : out std_logic;
   c3_p0_wr_clk                            : in std_logic;
   c3_p0_wr_en                             : in std_logic;
   c3_p0_wr_mask                           : in std_logic_vector(C3_P0_MASK_SIZE - 1 downto 0);
   c3_p0_wr_data                           : in std_logic_vector(C3_P0_DATA_PORT_SIZE - 1 downto 0);
   c3_p0_wr_full                           : out std_logic;
   c3_p0_wr_empty                          : out std_logic;
   c3_p0_wr_count                          : out std_logic_vector(6 downto 0);
   c3_p0_wr_underrun                       : out std_logic;
   c3_p0_wr_error                          : out std_logic;
   c3_p0_rd_clk                            : in std_logic;
   c3_p0_rd_en                             : in std_logic;
   c3_p0_rd_data                           : out std_logic_vector(C3_P0_DATA_PORT_SIZE - 1 downto 0);
   c3_p0_rd_full                           : out std_logic;
   c3_p0_rd_empty                          : out std_logic;
   c3_p0_rd_count                          : out std_logic_vector(6 downto 0);
   c3_p0_rd_overflow                       : out std_logic;
   c3_p0_rd_error                          : out std_logic;
   c3_p1_cmd_clk                           : in std_logic;
   c3_p1_cmd_en                            : in std_logic;
   c3_p1_cmd_instr                         : in std_logic_vector(2 downto 0);
   c3_p1_cmd_bl                            : in std_logic_vector(5 downto 0);
   c3_p1_cmd_byte_addr                     : in std_logic_vector(29 downto 0);
   c3_p1_cmd_empty                         : out std_logic;
   c3_p1_cmd_full                          : out std_logic;
   c3_p1_wr_clk                            : in std_logic;
   c3_p1_wr_en                             : in std_logic;
   c3_p1_wr_mask                           : in std_logic_vector(C3_P1_MASK_SIZE - 1 downto 0);
   c3_p1_wr_data                           : in std_logic_vector(C3_P1_DATA_PORT_SIZE - 1 downto 0);
   c3_p1_wr_full                           : out std_logic;
   c3_p1_wr_empty                          : out std_logic;
   c3_p1_wr_count                          : out std_logic_vector(6 downto 0);
   c3_p1_wr_underrun                       : out std_logic;
   c3_p1_wr_error                          : out std_logic;
   c3_p1_rd_clk                            : in std_logic;
   c3_p1_rd_en                             : in std_logic;
   c3_p1_rd_data                           : out std_logic_vector(C3_P1_DATA_PORT_SIZE - 1 downto 0);
   c3_p1_rd_full                           : out std_logic;
   c3_p1_rd_empty                          : out std_logic;
   c3_p1_rd_count                          : out std_logic_vector(6 downto 0);
   c3_p1_rd_overflow                       : out std_logic;
   c3_p1_rd_error                          : out std_logic);
end component;

begin
   
u_mem32 : mem32
   port map (
      c3_sys_clk          => clk_sys,
      c3_sys_rst_i        => reset,
      
      c3_clk0             => clk_sys_out,
      mcb_drp_clk           => clk_calibration,
      c3_rst0             => open,
      c3_calib_done       => calib_done,

      mcb3_dram_dq        => mcb3_dram_dq,  
      mcb3_dram_a         => mcb3_dram_a,  
      mcb3_dram_ba        => mcb3_dram_ba,
      mcb3_dram_ras_n     => mcb3_dram_ras_n,                        
      mcb3_dram_cas_n     => mcb3_dram_cas_n,                        
      mcb3_dram_we_n      => mcb3_dram_we_n,                          
      mcb3_dram_cke       => mcb3_dram_cke,                          
      mcb3_dram_ck        => mcb3_dram_ck,                          
      mcb3_dram_ck_n      => mcb3_dram_ck_n,       
      mcb3_dram_dqs       => mcb3_dram_dqs,                          
      mcb3_dram_udqs      => mcb3_dram_udqs,    -- for X16 parts           
      mcb3_dram_udm       => mcb3_dram_udm,     -- for X16 parts
      mcb3_dram_dm        => mcb3_dram_dm,
      mcb3_rzq            => mcb3_rzq,
      ---------------------------------------------------
      c3_p0_cmd_clk       =>  clk_reader,
      c3_p0_cmd_en        =>  read_cmd_enable,
      c3_p0_cmd_instr     =>  "001", -- read
      c3_p0_cmd_bl        =>  "001111",  -- 16 words
      c3_p0_cmd_byte_addr =>  read_cmd_address,
      c3_p0_cmd_empty     =>  read_cmd_empty,
      c3_p0_cmd_full      =>  read_cmd_full,

      c3_p0_wr_clk        =>  clk_reader,
      c3_p0_wr_en         =>  '0',
      c3_p0_wr_mask       =>  (others => '0'),
      c3_p0_wr_data       =>  (others => '0'),
      c3_p0_wr_full       =>  open,
      c3_p0_wr_empty      =>  open,
      c3_p0_wr_count      =>  open,
      c3_p0_wr_underrun   =>  open,
      c3_p0_wr_error      =>  open,
   
      ---------------------------------------------------
      c3_p0_rd_clk        =>  clk_reader,       -- data FIFO clock
      c3_p0_rd_en         =>  read_data_enable, -- read enable
      c3_p0_rd_data       =>  read_data,        -- read data
      c3_p0_rd_full       =>  read_data_full,   -- read FIFO full
      c3_p0_rd_empty      =>  read_data_empty,  -- read FIFO empty
      c3_p0_rd_count      =>  read_data_count,  -- count in read FIFO
      c3_p0_rd_overflow   =>  read_overflow,    -- read fifo overflow
      c3_p0_rd_error      =>  read_error,       -- read fifo error

      ---------------------------------------------------
      c3_p1_cmd_clk       =>  clk_writer,
      c3_p1_cmd_en        =>  write_cmd_enable,
      c3_p1_cmd_instr     =>  "000", -- write
      c3_p1_cmd_bl        =>  "000000",  -- 1 word
      c3_p1_cmd_byte_addr =>  write_cmd_address,
      c3_p1_cmd_empty     =>  write_cmd_empty,
      c3_p1_cmd_full      =>  write_cmd_full,
    
      c3_p1_wr_clk        =>  clk_writer,
      c3_p1_wr_en         =>  write_data_enable,
      c3_p1_wr_mask       =>  write_mask,
      c3_p1_wr_data       =>  write_data,
      c3_p1_wr_full       =>  write_data_full,
      c3_p1_wr_empty      =>  write_data_empty,
      c3_p1_wr_count      =>  open,
      c3_p1_wr_underrun   =>  open,
      c3_p1_wr_error      =>  write_error,
   
      c3_p1_rd_clk        =>  clk_writer,
      c3_p1_rd_en         =>  '0', 
      c3_p1_rd_data       =>  open,
      c3_p1_rd_full       =>  open,
      c3_p1_rd_empty      =>  open,
      c3_p1_rd_count      =>  open,
      c3_p1_rd_overflow   =>  open,
      c3_p1_rd_error      =>  open
   );     
end Behavioral;

top_level.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
--
-- Module : top_level.vhd
-- 
-- Description: Test of a MCB based 1280x720 frame buffer.
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity top_level is
    Port ( 
      clk_50          : in  STD_LOGIC;
      -- VGA Port
      hsync           : out STD_LOGIC;
      vsync           : out STD_LOGIC;
      red             : out std_logic_vector(2 downto 0);      
      green           : out std_logic_vector(2 downto 0);
      blue            : out std_logic_vector(2 downto 1);
      -- Status LEDs
      led_calibrate   : out STD_LOGIC;
      led_written     : out STD_LOGIC;
      -- Memory Signals
      mcb3_dram_dq    : inout std_logic_vector(15 downto 0);
      mcb3_dram_a     : out   std_logic_vector(12 downto 0);
      mcb3_dram_ba    : out   std_logic_vector( 1 downto 0);
      mcb3_dram_cke   : out   std_logic;
      mcb3_dram_ras_n : out   std_logic;
      mcb3_dram_cas_n : out   std_logic;
      mcb3_dram_we_n  : out   std_logic;
      mcb3_dram_dm    : out   std_logic;
      mcb3_dram_udqs  : inout std_logic;
      mcb3_rzq        : inout std_logic;
      mcb3_dram_udm   : out   std_logic;
      mcb3_dram_dqs   : inout std_logic;
      mcb3_dram_ck    : out   std_logic;
      mcb3_dram_ck_n  : out   std_logic
    );
end top_level;

architecture Behavioral of top_level is
   -- Timings for 1280x720@60Hz, 75Mhz pixel clock
   constant hVisible    : natural := 1280;
   constant hSyncStart  : natural := 1352;
   constant hSyncEnd    : natural := 1432;
   constant hMax        : natural := 1647;
   constant hSyncActive : std_logic := '1';

   constant vVisible    : natural := 720;
   constant vSyncStart  : natural := 723;
   constant vSyncEnd    : natural := 728;
   constant vMax        : natural := 750;
   constant vSyncActive : std_logic := '1';

   COMPONENT mem_wrapper
   PORT(
      clk_sys     : IN  std_logic;
      clk_sys_out : OUT std_logic;
      clk_reader  : IN  std_logic;
      clk_writer  : IN  std_logic;
      clk_calibration : out std_logic;

      write_cmd_enable  : IN  std_logic;
      write_cmd_address : IN  std_logic_vector(29 downto 0);
      write_cmd_empty   : OUT std_logic;
      write_cmd_full    : OUT std_logic;
      write_data_empty  : OUT std_logic;
      write_data_full   : OUT std_logic;
      write_data_count  : OUT std_logic_vector(6 downto 0);
      write_data_enable : IN  std_logic;
      write_mask        : in  std_logic_vector(3 downto 0);
      write_data        : IN  std_logic_vector(31 downto 0);
      write_error       : OUT std_logic;

      read_cmd_enable   : IN  std_logic;
      read_cmd_address  : IN  std_logic_vector(29 downto 0);
      read_data_enable  : IN  std_logic;    
      read_cmd_empty    : OUT std_logic;
      read_cmd_full     : OUT std_logic;
      read_data         : OUT std_logic_vector(31 downto 0);
      read_data_empty   : OUT std_logic;
      read_data_full    : OUT std_logic;
      read_data_count   : OUT std_logic_vector(6 downto 0);
      read_overflow     : OUT std_logic;
      read_error        : OUT std_logic;

      mcb3_dram_dq     : INOUT std_logic_vector(15 downto 0);
      mcb3_dram_udqs   : INOUT std_logic;
      mcb3_rzq         : INOUT std_logic;
      mcb3_dram_dqs    : INOUT std_logic;      
      mcb3_dram_a      : OUT   std_logic_vector(12 downto 0);
      mcb3_dram_ba     : OUT   std_logic_vector(1 downto 0);
      mcb3_dram_cke    : OUT   std_logic;
      mcb3_dram_ras_n  : OUT   std_logic;
      mcb3_dram_cas_n  : OUT   std_logic;
      mcb3_dram_we_n   : OUT   std_logic;
      mcb3_dram_dm     : OUT   std_logic;
      mcb3_dram_udm    : OUT   std_logic;
      mcb3_dram_ck     : OUT   std_logic;
      mcb3_dram_ck_n   : OUT   std_logic;
      
      reset            : IN    std_logic;
      calib_done       : OUT   std_logic
      );
   END COMPONENT;

   COMPONENT Test_pattern_writer GENERIC (
		hVisible : natural;
		vVisible : natural
	);
	PORT(
      clk               : IN  std_logic;
      completed         : OUT std_logic;
      memory_ready      : IN  std_logic;
      write_cmd_empty   : IN  std_logic;
      write_cmd_full    : IN  std_logic;
      write_data_empty  : IN  std_logic;
      write_data_count  : IN  std_logic_vector(6 downto 0);          
      write_cmd_enable  : OUT std_logic;
      write_cmd_address : OUT std_logic_vector(29 downto 0);
      write_data_enable : OUT std_logic;
      write_mask        : OUT std_logic_vector(3 downto 0);
      write_data        : OUT std_logic_vector(31 downto 0)
      );
   END COMPONENT;

   COMPONENT mcb_vga
	GENERIC (
		-- Timings for 1280x720@60Hx
		hVisible    : natural;
		hSyncStart  : natural;
		hSyncEnd    : natural;
		hMax        : natural;
		hSyncActive : std_logic;

		vVisible    : natural;
		vSyncStart  : natural;
		vSyncEnd    : natural;
		vMax        : natural;
		vSyncActive : std_logic
	);
	PORT (
      clk_reader : IN std_logic;
      
      hsync           : OUT std_logic;
      vsync           : OUT std_logic;
      red             : OUT std_logic_vector(2 downto 0);
      green           : OUT std_logic_vector(2 downto 0);      
      blue            : OUT std_logic_vector(2 downto 1);
      
      memory_ready     : IN  std_logic;

      read_cmd_enable  : OUT std_logic;
      read_cmd_address : OUT std_logic_vector(29 downto 0);
      read_data_enable : OUT std_logic;          
      
      read_cmd_full   : IN std_logic;
      read_cmd_empty  : IN std_logic;
      read_data       : IN std_logic_vector(31 downto 0);
      read_data_empty : IN std_logic;
      read_data_full  : IN std_logic;
      read_data_count : IN std_logic_vector(6 downto 0)
      );
   END COMPONENT;

	-- clocks --
   signal clk_75            : std_logic;
   signal clk_reader        : std_logic;
   signal clk_writer        : std_logic;

	-- Read port signals --
   signal read_cmd_enable   : std_logic := '0';
   signal read_cmd_address  : std_logic_vector(29 downto 0) := (others => '0');
   signal read_cmd_empty    : std_logic;
   signal read_cmd_full     : std_logic;
   signal read_error        : std_logic;

   signal read_data         : std_logic_vector(31 downto 0);
   signal read_data_enable  : std_logic := '0';
   signal read_data_empty   : std_logic;
   signal read_data_full    : std_logic;
   signal read_data_count   : std_logic_vector(6 downto 0);
   signal read_overflow     : std_logic;

	-- Write port signals --
   signal write_cmd_enable  : std_logic := '0';
   signal write_cmd_address : std_logic_vector(29 downto 0)  := (others => '0');
   signal write_cmd_empty   : std_logic;
   signal write_cmd_full    : std_logic;
   signal write_error       : std_logic;
      
   signal write_data_empty  : std_logic;
   signal write_data_full   : std_logic;
   signal write_data_count  : std_logic_vector(6 downto 0);
   signal write_data_enable : std_logic := '0';
   signal write_mask        : std_logic_vector(3 downto 0);
   signal write_data        : std_logic_vector(31 downto 0) := (others => '0');
	
	-- Status signals --
   signal memory_ready      : std_logic;
   signal memory_written    : std_logic;
   signal reset             : std_logic := '0';

begin
   led_calibrate <= memory_ready;  -- this LED is red  (L2)
   led_written   <= memory_written;  -- this LED is green(L1)   
   
   clk_writer <= clk_75;
   clk_reader <= clk_75;
   
   Inst_mem_wrapper: mem_wrapper PORT MAP(
      clk_sys     => clk_50,
      clk_sys_out => open,
      clk_calibration => clk_75,
      
      -- Write port
      clk_writer        => clk_writer,
      write_cmd_enable  => write_cmd_enable,
      write_cmd_empty   => write_cmd_empty,
      write_cmd_full    => write_cmd_full,
      write_cmd_address => write_cmd_address,
      write_error       => write_error,
      
      write_data_enable => write_data_enable,
      write_mask        => write_mask,
      write_data        => write_data,
      write_data_full   => write_data_full,
      write_data_empty  => write_data_empty,
      write_data_count  => write_data_count,
      
      -- Read port
      clk_reader        => clk_reader,
      read_cmd_enable   => read_cmd_enable,
      read_cmd_address  => read_cmd_address,
      read_cmd_full     => read_cmd_full,
      read_cmd_empty    => read_cmd_empty,
      read_error        => read_error,
      read_overflow     => read_overflow,
      
      read_data_enable  => read_data_enable,
      read_data         => read_data,
      read_data_full    => read_data_full,
      read_data_empty   => read_data_empty,
      read_data_count   => read_data_count,
      
      -- Memory chip interface
      mcb3_dram_dq      => mcb3_dram_dq,
      mcb3_dram_a       => mcb3_dram_a,
      mcb3_dram_ba      => mcb3_dram_ba,
      mcb3_dram_cke     => mcb3_dram_cke,
      mcb3_dram_ras_n   => mcb3_dram_ras_n,
      mcb3_dram_cas_n   => mcb3_dram_cas_n,
      mcb3_dram_we_n    => mcb3_dram_we_n,
      mcb3_dram_dm      => mcb3_dram_dm,
      mcb3_dram_udqs    => mcb3_dram_udqs,
      mcb3_rzq          => mcb3_rzq,
      mcb3_dram_udm     => mcb3_dram_udm,
      mcb3_dram_dqs     => mcb3_dram_dqs,
      mcb3_dram_ck      => mcb3_dram_ck,
      mcb3_dram_ck_n    => mcb3_dram_ck_n,
      
      -- Status
      calib_done => memory_ready,
      
      -- reset
      reset => reset
   );
   
   Inst_Test_pattern_writer: Test_pattern_writer GENERIC MAP (
		hVisible => hVisible,
		vVisible => vVisible
	) PORT MAP(
      clk               => clk_writer,
      completed         => memory_written,
      memory_ready      => memory_ready,
      write_cmd_enable  => write_cmd_enable,
      write_cmd_address => write_cmd_address,
      write_cmd_empty   => write_cmd_empty,
      write_cmd_full    => write_cmd_full,
      write_data_empty  => write_data_empty,
      write_data_count  => write_data_count,
      write_data_enable => write_data_enable,
      write_mask        => write_mask,
      write_data        => write_data
   );

inst_vga: mcb_vga GENERIC MAP (
		hVisible    => hVisible,
		hSyncStart  => hSyncStart,
		hSyncEnd    => hSyncEnd,
		hMax        => hMax,
		hSyncActive => hSyncActive,

		vVisible    => vVisible,
		vSyncStart  => vSyncStart,
		vSyncEnd    => vSyncEnd,
		vMax        => vMax,
		vSyncActive => vSyncActive
	) PORT MAP (
      hsync => hsync,
      vsync => vsync,
      red   => red,
      green => green,
      blue  => blue,

		-- Only start reading pixels once the frame buffer has been written
      memory_ready      => memory_written,

      -- Read port
      clk_reader        => clk_reader,
      read_cmd_enable   => read_cmd_enable,
      read_cmd_address  => read_cmd_address,
      read_cmd_full     => read_cmd_full,
      read_cmd_empty    => read_cmd_empty,      
      read_data_enable  => read_data_enable,
      read_data         => read_data,
      read_data_full    => read_data_full,
      read_data_empty   => read_data_empty,
      read_data_count   => read_data_count
   );
end Behavioral;

Personal tools