module iahb_mem_ctrl(
  lite_mmc_hsel,
  lite_yy_haddr,
  lite_yy_hsize,
  lite_yy_htrans,
  lite_yy_hwdata,
  lite_yy_hwrite,
  mmc_lite_hrdata,
  mmc_lite_hready,
  mmc_lite_hresp,
  pad_biu_bigend_b,
  pad_cpu_rst_b,
  pll_core_cpuclk
);

// &Ports; @22
input           lite_mmc_hsel;                 
input   [31:0]  lite_yy_haddr;                 
input   [2 :0]  lite_yy_hsize;                 
input   [1 :0]  lite_yy_htrans;                
input   [31:0]  lite_yy_hwdata;                
input           lite_yy_hwrite;                
input           pad_biu_bigend_b;              
input           pad_cpu_rst_b;                 
input           pll_core_cpuclk;               
output  [31:0]  mmc_lite_hrdata;               
output          mmc_lite_hready;               
output  [1 :0]  mmc_lite_hresp;                

// &Regs; @23
reg     [15:0]  addr_holding;                  
reg     [3 :0]  lite_mem_wen;                  
reg             lite_read_bypass;              
reg             lite_read_bypass_vld;          
reg             lite_read_stall;               
reg             lite_read_stall_vld;           
reg     [31:0]  lite_wbuf_addr;                
reg     [31:0]  lite_wbuf_data;                
reg     [2 :0]  lite_wbuf_size;                
reg             lite_write_req;                
reg             lite_write_stall;              

// &Wires; @24
wire            lite_addr_hit;                 
wire            lite_addr_no_hit;              
wire    [31:0]  lite_bypass_data;              
wire    [31:0]  lite_mem_addr;                 
wire            lite_mem_cen;                  
wire    [31:0]  lite_mem_din;                  
wire    [31:0]  lite_mem_dout;                 
wire            lite_mmc_hsel;                 
wire            lite_read_addr_hit_with_bypass; 
wire            lite_read_addr_hit_with_stall; 
wire            lite_read_req;                 
wire            lite_wbuf_update;              
wire            lite_write_cplt;               
wire            lite_write_en;                 
wire            lite_write_req_en;             
wire            lite_write_stall_en;           
wire    [31:0]  lite_yy_haddr;                 
wire    [2 :0]  lite_yy_hsize;                 
wire    [31:0]  lite_yy_hwdata;                
wire            lite_yy_hwrite;                
wire    [31:0]  mmc_lite_hrdata;               
wire            mmc_lite_hready;               
wire    [1 :0]  mmc_lite_hresp;                
wire            pad_biu_bigend_b;              
wire            pad_cpu_rst_b;                 
wire            pll_core_cpuclk;               
wire    [7 :0]  ram0_din;                      
wire    [7 :0]  ram0_dout;                     
wire    [7 :0]  ram1_din;                      
wire    [7 :0]  ram1_dout;                     
wire    [7 :0]  ram2_din;                      
wire    [7 :0]  ram2_dout;                     
wire    [7 :0]  ram3_din;                      
wire    [7 :0]  ram3_dout;                     
wire    [15:0]  ram_addr;                      
wire            ram_clk;                       
wire    [3 :0]  ram_wen;                       

parameter IMEM_WIDTH = 18;

// &Force("nonport","lite_mem_addr"); @27
// &Force("nonport","lite_mem_cen"); @28
// &Force("nonport","lite_mem_din"); @29
// &Force("nonport","lite_mem_dout"); @30
// &Force("nonport","lite_mem_wen"); @31
// &Force("input","lite_yy_htrans"); @32
// &Force("bus", "lite_yy_htrans", 1, 0); @33
// //&Force("nonport",""); @34
// //&Force("nonport",""); @35
// //&Force("nonport",""); @36
// //&Force("nonport",""); @37
// //&Force("nonport",""); @38
// //&Force("nonport",""); @39
// //&Force("nonport",""); @40


//write buffer
assign lite_wbuf_update = lite_yy_hwrite && lite_mmc_hsel;
always @ (posedge pll_core_cpuclk or negedge pad_cpu_rst_b)
begin
  if(!pad_cpu_rst_b)
  begin
    lite_wbuf_addr[31:0] <= 32'b0;
    lite_wbuf_size[2:0]  <= 3'b0;
  end
  else if (lite_wbuf_update)
  begin
    lite_wbuf_addr[31:0] <= lite_yy_haddr;
    lite_wbuf_size[2:0]  <= lite_yy_hsize;
  end
  //else if (lite_write_cplt)
  //begin
  //  lite_wbuf_addr[31:0] <= 32'b0;
  //  lite_wbuf_size[2:0]  <= 3'b0;
  //end
end    
//write data    
always @ (posedge pll_core_cpuclk or negedge pad_cpu_rst_b)
begin
  if(!pad_cpu_rst_b)
  begin
    lite_wbuf_data[31:0] <= 32'b0;
  end
  else if (lite_write_req)
  begin
    lite_wbuf_data[31:0] <= lite_yy_hwdata[31:0];
  end
end
//read first and wirte will stall when address don't hit
assign lite_write_stall_en = lite_write_req && lite_addr_no_hit && lite_read_req;
always @ (posedge pll_core_cpuclk or negedge pad_cpu_rst_b)
begin
  if(!pad_cpu_rst_b)
  begin
    lite_write_stall <= 1'b0;
  end
  else if (lite_write_stall_en)
  begin
    lite_write_stall <= 1'b1;
  end
  else if (lite_write_cplt)
  begin
     lite_write_stall <=1'b0;
  end   
end
//write request from bus interface
assign lite_write_req_en = lite_yy_hwrite && lite_mmc_hsel;
always @ (posedge pll_core_cpuclk or negedge pad_cpu_rst_b)
begin
  if(!pad_cpu_rst_b)
  begin
    lite_write_req <= 1'b0;
  end
  else if (lite_write_req_en)
  begin
    lite_write_req <= 1'b1;
  end
  else
  begin
    lite_write_req <= 1'b0;
  end 
end
//read bypass and read stall
always @(posedge pll_core_cpuclk or negedge pad_cpu_rst_b)
begin
  if(!pad_cpu_rst_b)
  begin
    lite_read_bypass <= 1'b0;
    lite_read_stall  <= 1'b0;
  end
  else
  begin
    lite_read_bypass <= lite_read_addr_hit_with_bypass;
    lite_read_stall  <= lite_read_addr_hit_with_stall;
  end
end

//no read first and write request or write stall 
assign lite_write_en    = ((lite_write_req | lite_write_stall) && (lite_addr_hit | (lite_yy_hwrite && lite_mmc_hsel) | ~lite_mmc_hsel));
assign lite_bypass_data[31:0] = lite_wbuf_data[31:0];
//write complete 
assign lite_write_cplt  = (lite_write_req | lite_write_stall) && (lite_addr_hit | ~lite_mmc_hsel | (lite_yy_hwrite && lite_mmc_hsel));
//read request
assign lite_read_req    = ~lite_yy_hwrite && lite_mmc_hsel;
//address hit
assign lite_addr_no_hit = (lite_yy_haddr[31:2] != lite_wbuf_addr[31:2]);
assign lite_addr_hit    = ~lite_addr_no_hit;
//address hit and read will bypass
assign lite_read_addr_hit_with_bypass = lite_read_req && (lite_write_req | lite_write_stall) && lite_addr_hit && lite_read_bypass_vld;
//address hit but read will stall
assign lite_read_addr_hit_with_stall  = lite_read_req && (lite_write_req | lite_write_stall) && lite_addr_hit && lite_read_stall_vld;

//read bypass or stall
// &CombBeg; @140
always @( lite_yy_haddr[1:0]
       or lite_wbuf_addr[1:0]
       or lite_wbuf_size[2:0]
       or lite_yy_hsize[2:0])
begin
casez({lite_wbuf_size[2:0],lite_yy_hsize[2:0],lite_wbuf_addr[1:0],lite_yy_haddr[1:0]})
//st.b/ld.b
10'b000_000_00_00,
10'b000_000_01_01,
10'b000_000_10_10,
10'b000_000_11_11:
  begin
    lite_read_bypass_vld = 1'b1;
    lite_read_stall_vld = 1'b0;
  end
//st.b/ld.h
10'b000_001_??_??:
  begin
    lite_read_stall_vld = 1'b1;
    lite_read_bypass_vld = 1'b0;
  end
//st.b/ld.w
10'b000_010_??_??:
  begin
    lite_read_stall_vld = 1'b1;
    lite_read_bypass_vld = 1'b0;
  end
//st.h/ld.b
10'b001_000_0?_0?,
10'b001_000_1?_1?:
  begin
    lite_read_bypass_vld = 1'b1;
    lite_read_stall_vld = 1'b0;
  end
//st.h/ld.h
10'b001_001_0?_0?,
10'b001_001_1?_1?:
  begin
    lite_read_bypass_vld = 1'b1;
    lite_read_stall_vld = 1'b0;
  end
//st.h/ld.w
10'b001_010_??_??:
  begin
    lite_read_stall_vld = 1'b1;
    lite_read_bypass_vld = 1'b0;
  end
//st.w/all lds
10'b010_???_??_??:
  begin
    lite_read_bypass_vld = 1'b1;
    lite_read_stall_vld = 1'b0;
  end
default:
  begin
    lite_read_bypass_vld = 1'b0;
    lite_read_stall_vld = 1'b0;
  end
endcase
// &CombEnd; @195
end



//memory select
assign lite_mem_cen = ~(lite_read_req | lite_write_req | lite_write_stall);
//memory write enable
// &CombBeg; @202
always @( lite_wbuf_addr[1:0]
       or lite_wbuf_size[2:0]
       or pad_biu_bigend_b
       or lite_write_en)
begin
  case({pad_biu_bigend_b, lite_write_en, lite_wbuf_size[2:0], lite_wbuf_addr[1:0]})
  7'b0100000: 
    begin 
      lite_mem_wen[3:0] = 4'b0111;
    end
  7'b0100001:
    begin 
      lite_mem_wen[3:0] = 4'b1011;
    end
  7'b0100010:
    begin
      lite_mem_wen[3:0] = 4'b1101;
    end
  7'b0100011:
    begin   
      lite_mem_wen[3:0] = 4'b1110;
    end
  7'b0100100:
    begin
      lite_mem_wen[3:0] = 4'b0011;
    end
  7'b0100110:
    begin
      lite_mem_wen[3:0] = 4'b1100;
    end
  7'b0101000:
    begin
      lite_mem_wen[3:0] = 4'b0000;
    end
  7'b1100000:
    begin
      lite_mem_wen[3:0] = 4'b1110;
    end
  7'b1100001:
    begin
      lite_mem_wen[3:0] = 4'b1101;
    end
  7'b1100010:
    begin
      lite_mem_wen[3:0] = 4'b1011;
    end
  7'b1100011:
    begin
      lite_mem_wen[3:0] = 4'b0111;
    end
  7'b1100100:
    begin
      lite_mem_wen[3:0] = 4'b1100;
    end
  7'b1100110:
    begin
      lite_mem_wen[3:0] = 4'b0011;
    end
  7'b1101000:
     begin
       lite_mem_wen[3:0] = 4'b0000;
     end
  default:
    begin
      lite_mem_wen[3:0] = 4'b1111;
    end
  endcase
// &CombEnd; @265
end

assign lite_mem_addr[31:0] = (lite_write_en | lite_read_stall) ? lite_wbuf_addr[31:0] : lite_yy_haddr[31:0];
assign lite_mem_din[31:0] = (lite_write_stall) ? lite_wbuf_data[31:0] : lite_yy_hwdata[31:0];
assign mmc_lite_hrdata[31:0] = lite_read_bypass ? lite_bypass_data[31:0] : lite_mem_dout[31:0];
assign mmc_lite_hready       = !lite_read_stall;
assign mmc_lite_hresp[1:0]   = 2'b0;



//memory
always @(posedge pll_core_cpuclk)
begin
  if(!lite_mem_cen)
    addr_holding[IMEM_WIDTH-3:0] <= lite_mem_addr[IMEM_WIDTH-1:2];
end

assign ram_clk = pll_core_cpuclk;
assign ram_addr[IMEM_WIDTH-3:0] = lite_mem_cen ? addr_holding[IMEM_WIDTH-3:0] : lite_mem_addr[IMEM_WIDTH-1:2];
assign ram_wen[0] = !lite_mem_cen && !lite_mem_wen[0];
assign ram_wen[1] = !lite_mem_cen && !lite_mem_wen[1];
assign ram_wen[2] = !lite_mem_cen && !lite_mem_wen[2];
assign ram_wen[3] = !lite_mem_cen && !lite_mem_wen[3];

assign ram0_din[7:0] = lite_mem_din[7:0];
assign ram1_din[7:0] = lite_mem_din[15:8];
assign ram2_din[7:0] = lite_mem_din[23:16];
assign ram3_din[7:0] = lite_mem_din[31:24];
assign lite_mem_dout[31:0] = {ram3_dout[7:0], ram2_dout[7:0], ram1_dout[7:0], ram0_dout[7:0]};

soc_fpga_ram #(8, IMEM_WIDTH-2) ram0(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr),
  .PortADataIn (ram0_din),
  .PortAWriteEnable(ram_wen[0]),
  .PortADataOut(ram0_dout));

soc_fpga_ram #(8, IMEM_WIDTH-2) ram1(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr),
  .PortADataIn (ram1_din),
  .PortAWriteEnable(ram_wen[1]),
  .PortADataOut(ram1_dout));

soc_fpga_ram #(8, IMEM_WIDTH-2) ram2(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr),
  .PortADataIn (ram2_din),
  .PortAWriteEnable(ram_wen[2]),
  .PortADataOut(ram2_dout));

soc_fpga_ram #(8, IMEM_WIDTH-2) ram3(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr),
  .PortADataIn (ram3_din),
  .PortAWriteEnable(ram_wen[3]),
  .PortADataOut(ram3_dout));

// &Force("nonport", "ram_addr"); @324
// &Force("nonport", "ram0_din"); @325
// &Force("nonport", "ram1_din"); @326
// &Force("nonport", "ram2_din"); @327
// &Force("nonport", "ram3_din"); @328
// &Force("nonport", "ram0_dout"); @329
// &Force("nonport", "ram1_dout"); @330
// &Force("nonport", "ram2_dout"); @331
// &Force("nonport", "ram3_dout"); @332
// &Force("nonport", "ram_wen"); @333
// &Force("nonport", "ram_clk"); @334
// &ModuleEnd; @335
endmodule