module C_SHIFT_RAM_V4_0 (A, D, CLK, CE, ACLR, ASET, AINIT, SCLR, SSET, SINIT, Q); 
    
	parameter C_ADDR_WIDTH    = 4;
	parameter C_AINIT_VAL     = "";
	parameter C_DEFAULT_DATA  = "0";
    parameter C_DEFAULT_DATA_RADIX = 1;
    parameter C_DEPTH         = 16;
	parameter C_ENABLE_RLOCS  = 1;
	parameter C_GENERATE_MIF  = 0;   // Unused by the behavioural model
    parameter C_HAS_A         = 0;
	parameter C_HAS_ACLR      = 0;
	parameter C_HAS_AINIT     = 0;
	parameter C_HAS_ASET      = 0;
	parameter C_HAS_CE        = 0;
	parameter C_HAS_SCLR      = 0;
	parameter C_HAS_SINIT     = 0;
	parameter C_HAS_SSET      = 0;
	parameter C_MEM_INIT_FILE = "null.mif";
	parameter C_MEM_INIT_RADIX = 1; // for backwards compatibility
    parameter C_READ_MIF      = 0;
    parameter C_REG_LAST_BIT  = 0;
	parameter C_SHIFT_TYPE    = 0; // c_fixed
	parameter C_SINIT_VAL     = "";
	parameter C_SYNC_ENABLE   = 0; // c_override
	parameter C_SYNC_PRIORITY = 1; // c_clear
    parameter C_WIDTH         = 16;
    	parameter C_DEPTH_TEMP = ((C_DEPTH/16)+1)*16; // to enable vsim to work
        parameter radix = (C_DEFAULT_DATA_RADIX == 1 ? C_MEM_INIT_RADIX : C_DEFAULT_DATA_RADIX);

    input  [C_WIDTH-1 : 0] D;
    input  [C_ADDR_WIDTH-1 : 0] A;
    input  CLK;
    input  CE;
    input  ACLR;
    input  ASET;
    input  AINIT;
    input  SCLR;
    input  SSET;
    input  SINIT;
    output [C_WIDTH-1 : 0] Q;
   
	wire [C_WIDTH-1 : 0] shift_out;
	wire [C_WIDTH-1 : 0] shift_out_1;
	wire [C_WIDTH-1 : 0] shift_out_2;
	wire [C_WIDTH-1 : 0] reg_out;
	// Internal values to drive signals when input is missing
	wire [C_WIDTH-1 : 0] intQ;
	wire [C_WIDTH-1 : 0] #1 Q = intQ;
	wire intCE;
    
	reg lastCLK;
	wire [C_WIDTH-1 : 0] feedin;
    wire [C_ADDR_WIDTH-1 : 0] addtop;
    wire [3 : 0] addlow;
    wire [C_ADDR_WIDTH-1 : 0] intA;
	
	integer i;
	integer rdeep;
	
    reg [C_WIDTH-1 : 0] default_data;
	reg [C_WIDTH-1 : 0] ram_data [0 : C_DEPTH];
	reg [C_WIDTH-1 : 0] shifter [0 : ((C_SHIFT_TYPE === 0 || C_DEPTH%16 == 0)?C_DEPTH:C_DEPTH_TEMP)];
    
    function integer ADDR_IS_X;
      input [C_ADDR_WIDTH-1 : 0] value;
      integer i;
    begin
      ADDR_IS_X = 0;
      for(i = 0; i < C_ADDR_WIDTH; i = i + 1)
        if(value[i] === 1'bX)
          ADDR_IS_X = 1;
    end
    endfunction

    // Sort out default values for missing ports
    
    assign intQ     = (C_REG_LAST_BIT === 1)?reg_out:shift_out;
	assign intCE = defval(CE, C_HAS_CE, 1);
	assign intA = (C_HAS_A ? A : C_DEPTH-1);
	assign addtop = (C_ADDR_WIDTH > 4 ? intA[C_ADDR_WIDTH-1 : (C_ADDR_WIDTH>4?4:0)] : 0);
 	assign addlow = intA[(C_ADDR_WIDTH>3?3:(C_ADDR_WIDTH-1)) : 0];	// modified by dlunn on 21/10/99 from intA[3:0]
	assign shift_out_1 = (ADDR_IS_X(intA) ? {C_WIDTH{1'bx}} : (intA < rdeep ? shifter[intA] : 1'bx));	// DLUNN MODIFIED FINAL EXPRESSION FROM 0 FOR ILLEGAL ADDRESSING
	assign shift_out_2 = (ADDR_IS_X(addlow) ? {C_WIDTH{1'bx}} : shifter[rdeep-16+addlow]);
	assign shift_out = ((C_DEPTH == 1 && C_REG_LAST_BIT) ? D[0] :
	                     C_SHIFT_TYPE == 0 ? shifter[C_DEPTH-C_REG_LAST_BIT-1] : 
					    (C_SHIFT_TYPE == 1 ? shift_out_1 : shift_out_2));
    assign feedin = (ADDR_IS_X(addtop) ? {C_WIDTH{1'bx}} : (addtop === 0 ? D : (addtop < rdeep/16 ? shifter[(addtop*16)-1] : {C_WIDTH{1'bx}})));	// DLUNN MODIFIED FINAL EXPRESSION FROM 0 FOR ILLEGAL ADDRESSING
	   
	// Register on output by default
	C_REG_FD_V4_0 #(C_AINIT_VAL, C_ENABLE_RLOCS, C_HAS_ACLR, C_HAS_AINIT, C_HAS_ASET,
			   C_HAS_CE, C_HAS_SCLR, C_HAS_SINIT, C_HAS_SSET,
			   C_SINIT_VAL, C_SYNC_ENABLE, C_SYNC_PRIORITY, C_WIDTH)
		final_reg (.D(shift_out), .CLK(CLK), .CE(CE), .ACLR(ACLR), .ASET(ASET),
			  .AINIT(AINIT), .SCLR(SCLR), .SSET(SSET), .SINIT(SINIT),
			  .Q(reg_out)); 
  
 
    initial
    begin
	  #1;
      rdeep = (C_SHIFT_TYPE === 0 || C_DEPTH%16 == 0)?C_DEPTH:((C_DEPTH/16)+1)*16;
      for(i = 0; i < rdeep; i = i + 1) shifter[i] = {C_WIDTH{1'b0}};
      default_data = 'b0;
    case (radix)
       3 : default_data = decstr_conv(C_DEFAULT_DATA);
       2 : default_data = binstr_conv(C_DEFAULT_DATA);
       1 : default_data = hexstr_conv(C_DEFAULT_DATA);
      default : $display("ERROR : BAD DATA RADIX");
    endcase

    for(i = 0; i < C_DEPTH; i = i + 1)
      ram_data[i] = default_data;
    
    if(C_READ_MIF == 1)
    begin
      $readmemb(C_MEM_INIT_FILE, ram_data);
    end
    if (C_GENERATE_MIF == 1)
      write_meminit_file;
	
    for(i = 0; i < C_DEPTH; i = i + 1)
      shifter[i] = ram_data[i];  
    end
	
    always@(posedge CLK or intA)
    begin
      if(CLK !== lastCLK && (intCE === 1'b1 && CLK !== 1'bx && lastCLK !== 1'bx))
      begin
        if(C_SHIFT_TYPE === 2)
		begin
        	for(i = rdeep-1; i > rdeep-16; i = i - 1) shifter[i] <= shifter[i-1];
			shifter[rdeep-16] <= feedin;
	        for(i = rdeep-17; i > 0; i = i - 1) shifter[i] <= shifter[i-1];
		end
		else 
	        for(i = rdeep-1; i > 0; i = i - 1) shifter[i] <= shifter[i-1];
		
		shifter[0] <= D;
      end
      if(CLK !== lastCLK && (intCE === 1'bx || CLK === 1'bx || lastCLK === 1'bx))
        for(i = 0; i < rdeep; i = i + 1) shifter[i] <= {C_WIDTH{1'bx}};
    end
      
  
	always@(CLK)
		lastCLK <= CLK;

	function defval;
	input i;
	input hassig;
	input val;
		begin
			if(hassig == 1)
				defval = i;
			else
				defval = val;
		end
	endfunction
	
	function [C_WIDTH-1:0] binstr_conv;
    input [(C_WIDTH*8)-1:0] def_data;

    integer index,i;

    begin
      index = 0;
      binstr_conv = 'b0;

      for( i=C_WIDTH-1; i>=0; i=i-1 )
      begin
        case (def_data[7:0])
          8'b00000000 : i = -1;
          8'b00110000 : binstr_conv[index] = 1'b0;
          8'b00110001 : binstr_conv[index] = 1'b1;
          default :
          begin
            $display("ERROR : NOT A BINARY CHARACTER");
            binstr_conv[index] = 1'bx;
          end
        endcase
        index = index + 1;
        def_data = def_data >> 8;
      end
    end
  endfunction

  function [C_WIDTH-1:0] hexstr_conv;
    input [(C_WIDTH*8)-1:0] def_data;

    integer index,i,j;
    reg [3:0] bin;

    begin
      index = 0;
      hexstr_conv = 'b0;
      for( i=C_WIDTH-1; i>=0; i=i-1 )
      begin
        case (def_data[7:0])
          8'b00000000 :
          begin
            bin = 4'b0000;
            i = -1;
          end
          8'b00110000 : bin = 4'b0000;
          8'b00110001 : bin = 4'b0001;
          8'b00110010 : bin = 4'b0010;
          8'b00110011 : bin = 4'b0011;
          8'b00110100 : bin = 4'b0100;
          8'b00110101 : bin = 4'b0101;
          8'b00110110 : bin = 4'b0110;
          8'b00110111 : bin = 4'b0111;
          8'b00111000 : bin = 4'b1000;
          8'b00111001 : bin = 4'b1001;
          8'b01000001 : bin = 4'b1010;
          8'b01000010 : bin = 4'b1011;
          8'b01000011 : bin = 4'b1100;
          8'b01000100 : bin = 4'b1101;
          8'b01000101 : bin = 4'b1110;
          8'b01000110 : bin = 4'b1111;
          8'b01100001 : bin = 4'b1010;
          8'b01100010 : bin = 4'b1011;
          8'b01100011 : bin = 4'b1100;
          8'b01100100 : bin = 4'b1101;
          8'b01100101 : bin = 4'b1110;
          8'b01100110 : bin = 4'b1111;
          default :
          begin
            $display("ERROR : NOT A HEX CHARACTER");
            bin = 4'bx;
          end
        endcase
        for( j=0; j<4; j=j+1)
        begin
          if ((index*4)+j < C_WIDTH)
          begin
            hexstr_conv[(index*4)+j] = bin[j];
          end
        end
        index = index + 1;
        def_data = def_data >> 8;
      end
    end
  endfunction

  function [C_WIDTH-1:0] decstr_conv;
    input [(C_WIDTH*8)-1:0] def_data;

    integer index,i,j;
    reg [3:0] bin;

    begin
      index = 0;
      decstr_conv = 'b0;
      for( i=C_WIDTH-1; i>=0; i=i-1 )
      begin
        case (def_data[7:0])
          8'b00000000 :
          begin
            bin = 4'b0000;
            i = -1;
          end
          8'b00110000 : bin = 4'b0000;
          8'b00110001 : bin = 4'b0001;
          8'b00110010 : bin = 4'b0010;
          8'b00110011 : bin = 4'b0011;
          8'b00110100 : bin = 4'b0100;
          8'b00110101 : bin = 4'b0101;
          8'b00110110 : bin = 4'b0110;
          8'b00110111 : bin = 4'b0111;
          8'b00111000 : bin = 4'b1000;
          8'b00111001 : bin = 4'b1001;
          8'b01000001 : bin = 4'b1010;
          8'b01000010 : bin = 4'b1011;
          8'b01000011 : bin = 4'b1100;
          8'b01000100 : bin = 4'b1101;
          8'b01000101 : bin = 4'b1110;
          8'b01000110 : bin = 4'b1111;
          default :
          begin
            $display("ERROR : NOT A DECIMAL CHARACTER");
            bin = 4'bx;
          end
        endcase
        for( j=0; j<4; j=j+1)
        begin
          if ((index*4)+j < C_WIDTH)
          begin
            decstr_conv[(index*4)+j] = bin[j];
          end
        end
        index = index + 1;
        def_data = def_data >> 8;
      end
    end
  endfunction
  
  task write_meminit_file;
  
    integer addrs, outfile, bit_index;
    
    reg [C_WIDTH-1 : 0] conts;
    reg anyX;
    
    begin
      outfile = $fopen(C_MEM_INIT_FILE);
      for( addrs = 0; addrs < C_DEPTH; addrs=addrs+1)
      begin
        anyX = 1'b0;
        conts = ram_data[addrs];
        for(bit_index = 0; bit_index < C_WIDTH; bit_index=bit_index+1)
          if(conts[bit_index] === 1'bx) anyX = 1'b1;
        if(anyX == 1'b1)  
          $display("ERROR : MEMORY CONTAINS UNKNOWNS");
        $fdisplay(outfile,"%b",ram_data[addrs]);
      end
      $fclose(outfile);
    end
  endtask

endmodule