-- =============================================================================================================
-- *
-- * Copyright (c) Mike
-- *
-- * File Name: ram_4Kx16_sim.vhd
-- *
-- * Version: V1.0
-- *
-- * Release Date:
-- *
-- * Author(s): M.Freeman
-- *
-- * Description: memory simulation 4K x 16bits
-- *
-- * Conditions of Use: THIS CODE IS COPYRIGHT AND IS SUPPLIED "AS IS" WITHOUT WARRANTY OF ANY KIND, INCLUDING,
-- *                    BUT NOT LIMITED TO, ANY IMPLIED WARRANTY OF MERCHANTABILITY AND FITNESS FOR A
-- *                    PARTICULAR PURPOSE.
-- *
-- * Notes: 
-- *
-- =============================================================================================================

use std.textio.all;

Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
use IEEE.std_logic_arith.all;

entity ram_4Kx16_sim is
  generic (  
    load_file_name      : string  := "iss/code/testcode_v1d.dat";
	 
    load_ppm_image      : boolean := False;
    load_ppm_image_addr : natural := 1024;     
    load_ppm_image_name : string  := "iss/image.ppm";

    save_ppm_image      : boolean := False;
    save_ppm_image_addr : natural := 1024;    
    save_ppm_image_rows : natural := 24;
    save_ppm_image_cols : natural := 24;     
    save_ppm_image_name : string  := "iss/output.ppm" );
  port (
    CLK      : in  std_logic;   
    ADDR_IN  : in  std_logic_vector( 11 downto 0 );
    DATA_IN  : in  std_logic_vector( 15 downto 0 );
    DATA_OUT : out std_logic_vector( 15 downto 0 );
    EN       : in  std_logic;     
    WE       : in  std_logic;
    DUMP     : in  std_logic );
end ram_4Kx16_sim;

architecture ram_4Kx16_sim_arch of ram_4Kx16_sim is
begin

  mem: process
    subtype word is std_logic_vector( 15 downto 0 );
    type mem_array is array (natural range 4095 downto 0) of word;

    variable mem: mem_array;
    variable address : natural;
    variable L : line;

    variable saved : boolean := False;
     
    --
    -- CONVERT NIBBLE TO CHAR
    --

    function hex_to_char (nibble: std_logic_vector(3 downto 0)) return character is
      variable hex: character;
    begin
      case nibble is
        when "0000" => hex := '0';
        when "0001" => hex := '1';
        when "0010" => hex := '2';
        when "0011" => hex := '3';
        when "0100" => hex := '4';
        when "0101" => hex := '5';
        when "0110" => hex := '6';
        when "0111" => hex := '7';
        when "1000" => hex := '8';
        when "1001" => hex := '9';
        when "1010" => hex := 'A';
        when "1011" => hex := 'B';
        when "1100" => hex := 'C';
        when "1101" => hex := 'D';
        when "1110" => hex := 'E';
        when "1111" => hex := 'F';
        when others => hex := 'X';
      end case;
      return hex;
    end hex_to_char;
	 
    --
    -- LOAD MEM
    --

    procedure load_mem( mem: inout mem_array) is
     
      file load_data_file : text open READ_MODE  is load_file_name;       
        
      variable data_string    : line;
      variable message_string : line;             
        
      variable addr, data, count : natural;
      variable data_char         : character;
      variable valid             : boolean;
        
      variable bin_addr : std_logic_vector(11 downto 0);
      variable bin_data : std_logic_vector(15 downto 0);    

    begin
      write( message_string, "ram initialization start" );
      writeline( output, message_string );

      for addr in 0 to 4095
      loop
        mem( addr ) := "0000000000000000";
      end loop; 

      while not endfile( load_data_file ) 
        loop
        readline( load_data_file, data_string );
        read( data_string, addr, valid);        -- address    
          
        if (valid)
        then             
          read( data_string, data_char );   -- space
             
          data := 0; 
          count := 0;

          while count < 16
          loop
            read( data_string, data_char );
            if data_char /= ' ' 
            then    
              count := count + 1;     -- count bits processed
              data := data + data;    -- shift string
              if data_char = '1' 
              then 
                data :=data + 1;      -- insert 1 if set
              end if;  
            end if;        
          end loop;      

          bin_data := conv_std_logic_vector( data, 16); 
          bin_addr := (conv_std_logic_vector( addr, 12));
          mem( addr ) := bin_data;	
    
        end if;        
      end loop;
    end load_mem;

          
    --
    -- LOAD PPM IMAGE
    --

    procedure load_ppm( mem: inout mem_array) is
     
      file image_data_file : text open READ_MODE  is load_ppm_image_name;    
        
      variable data_string : line;    
      variable message_string : line;
        
      variable addr, count, size, X, Y, R, G, B, MAX : natural;
      variable data_char : character;
      variable valid : boolean;
        
      variable bin_R : std_logic_vector(7 downto 0);
      variable bin_G : std_logic_vector(7 downto 0);
      variable bin_B : std_logic_vector(7 downto 0);        

    begin
      write( message_string, "load PPM image data into memory" );
      writeline( output, message_string );

      addr := load_ppm_image_addr;
        
      readline( image_data_file, data_string );
      read( data_string, data_char, valid);          -- P3
        
      if (valid and data_char = 'P')
      then
        readline( image_data_file, data_string );
        read( data_string, data_char, valid);   
          
        if (valid and data_char = '#')              -- Comment line
        then          
          readline( image_data_file, data_string );   -- image size X   
          read( data_string, X, valid); 

          write( message_string, "X = " );
          write( message_string, X );             

          if (valid)
          then
            read( data_string, Y, valid);           -- image size Y 
            write( message_string, "  Y = " );
            write( message_string, Y );             
            writeline( output, message_string );
                
            size := (X * Y);
                
            write( message_string , "image size = ");
            write( message_string , size);             
            writeline( output, message_string );
                
            readline( image_data_file, data_string );   -- max value
            read( data_string, MAX, valid); 
                
            if (valid)
            then    
              write( message_string, "MAX = " );
              write( message_string, MAX );             
              writeline( output, message_string );                
                
              count := 0;
              addr  := load_ppm_image_addr;            
                            
              while not endfile( image_data_file ) 
              loop
                readline( image_data_file, data_string );
                read( data_string, R, valid); 
                bin_R := conv_std_logic_vector( R, 8 );
                     
                read( data_string, G, valid);                     
                if (not valid)
                then
                  readline( image_data_file, data_string );
                  read( data_string, G, valid);
                end if;        
                bin_G := conv_std_logic_vector( G, 8 );
                     
                read( data_string, B, valid);
                if (not valid)
                then
                  readline( image_data_file, data_string );
                  read( data_string, B, valid);
                end if;    
                bin_B := conv_std_logic_vector( B, 8 );
 
                mem( addr ) := bin_R(7 DOWNTO 3) & bin_G(7 DOWNTO 2) & bin_B(7 DOWNTO 3);
                      
                addr := addr + 1;
                count := count + 1;              
              end  loop;
                    
              if (count = size)
              then
                write( message_string , "Pass : ");
              else
                write( message_string , "Fail : ");
              end if;
                      
              write( message_string , count);
              write( message_string , " ");
              write( message_string , size);                      
              writeline( output, message_string ); 
                     
            end if;
          end if;                
        end if;
      end if;

      write( message_string, "load PPM image data into memory finished" );
      writeline( output, message_string );

    end load_ppm;        
                  
    --
    -- SAVE PPM IMAGE
    --

    procedure save_ppm( mem: inout mem_array) is
     
      file image_data_file : text open WRITE_MODE is save_ppm_image_name;    
        
      variable data_string : line;    
      variable message_string : line;
        
      variable addr, count : natural;
      variable data_char : character;
      variable valid : boolean;
        
      variable bin_data : std_logic_vector(15 downto 0);
    
    begin
      write( message_string, "save PPM image data to file" );
      writeline( output, message_string );

      report "DUMP : " & time'image(now);
        
      addr := save_ppm_image_addr;

      write( data_string, "P3");
      writeline( image_data_file, data_string);
      write( data_string, "# MEM GENERATED PPM IMAGE");
      writeline( image_data_file, data_string);
      write( data_string , save_ppm_image_rows);
      write( data_string , ' ');        
      write( data_string , save_ppm_image_cols);
      writeline( image_data_file, data_string );
      write( data_string, "255");        
      writeline( image_data_file, data_string );
        
      for count in 0 to (save_ppm_image_rows * save_ppm_image_cols)-1 
      loop
        bin_data :=  mem( addr );
        addr := addr + 1;          
        write( data_string , ' ');         
        write( data_string , conv_integer( bin_data(15 downto 11) & "000" ) );
        write( data_string , ' ');    
        write( data_string , conv_integer( bin_data(10 downto 5) & "00" ) ); 
        write( data_string , ' ');         
        write( data_string , conv_integer( bin_data(4 downto 0) & "000" ) );
        writeline( image_data_file, data_string );          
      end loop;    
      write( message_string, "save PPM image data to file finished" );
      writeline( output, message_string );
    end save_ppm; 


  --
  -- MAIN
  --    

  begin
    load_mem( mem );
     
    if load_ppm_image
    then
      load_ppm( mem );
    end if;
	 
    loop
      if (EN = '1') 
      then  
        address := conv_integer( ADDR_IN );        
        if (WE = '1') 
        then
          mem( address ) := DATA_IN;
        end if;
        DATA_OUT <= mem( address );
      end if;
          
      if (DUMP = '1' and save_ppm_image and not saved)
      then
        saved := True;
        save_ppm( mem );  
      end if;

      wait on clk;

    end loop;
  end process;

end ram_4Kx16_sim_arch;


