module  ogfx_ram_arbiter (

    mclk,                                                // Main system clock
    puc_rst,                                             // Main system reset

   //------------------------------------------------------------

   // SW interface, fixed highest priority
    lut_ram_sw_addr_i,                                   // LUT-RAM Software address
    lut_ram_sw_din_i,                                    // LUT-RAM Software data
    lut_ram_sw_wen_i,                                    // LUT-RAM Software write strobe (active low)
    lut_ram_sw_cen_i,                                    // LUT-RAM Software chip enable (active low)
    lut_ram_sw_dout_o,                                   // LUT-RAM Software data input

   // Refresh-backend, fixed lowest priority
    lut_ram_refr_addr_i,                                 // LUT-RAM Refresh address
    lut_ram_refr_din_i,                                  // LUT-RAM Refresh data
    lut_ram_refr_wen_i,                                  // LUT-RAM Refresh write strobe (active low)
    lut_ram_refr_cen_i,                                  // LUT-RAM Refresh enable (active low)
    lut_ram_refr_dout_o,                                 // LUT-RAM Refresh data output
    lut_ram_refr_dout_rdy_nxt_o,                         // LUT-RAM Refresh data output ready during next cycle

   // LUT Memory interface
    lut_ram_addr_o,                                      // LUT-RAM address
    lut_ram_din_o,                                       // LUT-RAM data
    lut_ram_wen_o,                                       // LUT-RAM write strobe (active low)
    lut_ram_cen_o,                                       // LUT-RAM chip enable (active low)
    lut_ram_dout_i,                                      // LUT-RAM data input

   //------------------------------------------------------------

   // SW interface, fixed highest priority
    vid_ram_sw_addr_i,                                   // Video-RAM Software address
    vid_ram_sw_din_i,                                    // Video-RAM Software data
    vid_ram_sw_wen_i,                                    // Video-RAM Software write strobe (active low)
    vid_ram_sw_cen_i,                                    // Video-RAM Software chip enable (active low)
    vid_ram_sw_dout_o,                                   // Video-RAM Software data input

   // GPU interface (round-robin with refresh-backend)
    vid_ram_gpu_addr_i,                                  // Video-RAM GPU address
    vid_ram_gpu_din_i,                                   // Video-RAM GPU data
    vid_ram_gpu_wen_i,                                   // Video-RAM GPU write strobe (active low)
    vid_ram_gpu_cen_i,                                   // Video-RAM GPU chip enable (active low)
    vid_ram_gpu_dout_o,                                  // Video-RAM GPU data input
    vid_ram_gpu_dout_rdy_nxt_o,                          // Video-RAM GPU data output ready during next cycle

   // Refresh-backend (round-robin with GPU interface)
    vid_ram_refr_addr_i,                                 // Video-RAM Refresh address
    vid_ram_refr_din_i,                                  // Video-RAM Refresh data
    vid_ram_refr_wen_i,                                  // Video-RAM Refresh write strobe (active low)
    vid_ram_refr_cen_i,                                  // Video-RAM Refresh enable (active low)
    vid_ram_refr_dout_o,                                 // Video-RAM Refresh data output
    vid_ram_refr_dout_rdy_nxt_o,                         // Video-RAM Refresh data output ready during next cycle

   // Video Memory interface
    vid_ram_addr_o,                                      // Video-RAM address
    vid_ram_din_o,                                       // Video-RAM data
    vid_ram_wen_o,                                       // Video-RAM write strobe (active low)
    vid_ram_cen_o,                                       // Video-RAM chip enable (active low)
    vid_ram_dout_i                                       // Video-RAM data input

   //------------------------------------------------------------
);

// CLOCK/RESET
//=============
input                mclk;                               // Main system clock
input                puc_rst;                            // Main system reset

// LUT MEMORY
//=============

// SW interface, fixed highest priority
input  [`LRAM_MSB:0] lut_ram_sw_addr_i;                  // LUT-RAM Software address
input         [15:0] lut_ram_sw_din_i;                   // LUT-RAM Software data
input                lut_ram_sw_wen_i;                   // LUT-RAM Software write strobe (active low)
input                lut_ram_sw_cen_i;                   // LUT-RAM Software chip enable (active low)
output        [15:0] lut_ram_sw_dout_o;                  // LUT-RAM Software data input

// Refresh-backend, fixed lowest priority
input  [`LRAM_MSB:0] lut_ram_refr_addr_i;                // LUT-RAM Refresh address
input         [15:0] lut_ram_refr_din_i;                 // LUT-RAM Refresh data
input                lut_ram_refr_wen_i;                 // LUT-RAM Refresh write strobe (active low)
input                lut_ram_refr_cen_i;                 // LUT-RAM Refresh enable (active low)
output        [15:0] lut_ram_refr_dout_o;                // LUT-RAM Refresh data output
output               lut_ram_refr_dout_rdy_nxt_o;        // LUT-RAM Refresh data output ready during next cycle

// LUT Memory interface
output [`LRAM_MSB:0] lut_ram_addr_o;                     // LUT-RAM address
output        [15:0] lut_ram_din_o;                      // LUT-RAM data
output               lut_ram_wen_o;                      // LUT-RAM write strobe (active low)
output               lut_ram_cen_o;                      // LUT-RAM chip enable (active low)
input         [15:0] lut_ram_dout_i;                     // LUT-RAM data input

// VIDEO MEMORY
//==============

// SW interface, fixed highest priority
input  [`VRAM_MSB:0] vid_ram_sw_addr_i;                  // Video-RAM Software address
input         [15:0] vid_ram_sw_din_i;                   // Video-RAM Software data
input                vid_ram_sw_wen_i;                   // Video-RAM Software write strobe (active low)
input                vid_ram_sw_cen_i;                   // Video-RAM Software chip enable (active low)
output        [15:0] vid_ram_sw_dout_o;                  // Video-RAM Software data input

// GPU interface (round-robin with refresh-backend)
input  [`VRAM_MSB:0] vid_ram_gpu_addr_i;                 // Video-RAM GPU address
input         [15:0] vid_ram_gpu_din_i;                  // Video-RAM GPU data
input                vid_ram_gpu_wen_i;                  // Video-RAM GPU write strobe (active low)
input                vid_ram_gpu_cen_i;                  // Video-RAM GPU chip enable (active low)
output        [15:0] vid_ram_gpu_dout_o;                 // Video-RAM GPU data input
output               vid_ram_gpu_dout_rdy_nxt_o;         // Video-RAM GPU data output ready during next cycle

// Refresh-backend (round-robin with GPU interface)
input  [`VRAM_MSB:0] vid_ram_refr_addr_i;                // Video-RAM Refresh address
input         [15:0] vid_ram_refr_din_i;                 // Video-RAM Refresh data
input                vid_ram_refr_wen_i;                 // Video-RAM Refresh write strobe (active low)
input                vid_ram_refr_cen_i;                 // Video-RAM Refresh enable (active low)
output        [15:0] vid_ram_refr_dout_o;                // Video-RAM Refresh data output
output               vid_ram_refr_dout_rdy_nxt_o;        // Video-RAM Refresh data output ready during next cycle

// Video Memory interface
output [`VRAM_MSB:0] vid_ram_addr_o;                     // Video-RAM address
output        [15:0] vid_ram_din_o;                      // Video-RAM data
output               vid_ram_wen_o;                      // Video-RAM write strobe (active low)
output               vid_ram_cen_o;                      // Video-RAM chip enable (active low)
input         [15:0] vid_ram_dout_i;                     // Video-RAM data input


//=============================================================================
// 1)  WIRE, REGISTERS AND PARAMETER DECLARATION
//=============================================================================

reg     gpu_is_last_owner;


//=============================================================================
// 2) LUT MEMORY ARBITER
//=============================================================================

// Arbitration signals
wire    sw_lram_access_granted       = ~lut_ram_sw_cen_i;
wire    refr_lram_access_granted     = ~sw_lram_access_granted & ~lut_ram_refr_cen_i;

// LUT RAM signal muxing
assign  lut_ram_sw_dout_o            =  lut_ram_dout_i;

assign  lut_ram_refr_dout_o          =  lut_ram_dout_i;
assign  lut_ram_refr_dout_rdy_nxt_o  =  refr_lram_access_granted;

assign  lut_ram_addr_o               = ({`LRAM_AWIDTH{ sw_lram_access_granted  }} & lut_ram_sw_addr_i  ) |
                                       ({`LRAM_AWIDTH{ refr_lram_access_granted}} & lut_ram_refr_addr_i) ;

assign  lut_ram_din_o                = ({          16{ sw_lram_access_granted  }} & lut_ram_sw_din_i   ) |
                                       ({          16{ refr_lram_access_granted}} & lut_ram_refr_din_i ) ;

assign  lut_ram_wen_o                = (              ~sw_lram_access_granted     | lut_ram_sw_wen_i   ) &
                                       (              ~refr_lram_access_granted   | lut_ram_refr_wen_i ) ;

assign  lut_ram_cen_o                =  lut_ram_sw_cen_i & lut_ram_refr_cen_i;


//=============================================================================
// 3) VIDEO MEMORY ARBITER
//=============================================================================


// Arbitration signals
wire sw_vram_access_granted   = ~vid_ram_sw_cen_i;
wire gpu_vram_access_granted  = ~sw_vram_access_granted &                                           // No SW access
                                ((~vid_ram_gpu_cen_i &  vid_ram_refr_cen_i)                       | // GPU requests alone
                                 (~vid_ram_gpu_cen_i & ~vid_ram_refr_cen_i & ~gpu_is_last_owner)) ; // GPU & REFR both requests (arbitration required)

wire refr_vram_access_granted = ~sw_vram_access_granted &                                           // No SW access
                                (( vid_ram_gpu_cen_i & ~vid_ram_refr_cen_i)                       | // GPU requests alone
                                 (~vid_ram_gpu_cen_i & ~vid_ram_refr_cen_i &  gpu_is_last_owner)) ; // GPU & REFR both requests (arbitration required)

// Detect who was the last to own the RAM between the GPU and Refresh interface
always @ (posedge mclk or posedge puc_rst)
  if (puc_rst)                       gpu_is_last_owner <=  1'b0;
  else if (gpu_vram_access_granted ) gpu_is_last_owner <=  1'b1;
  else if (refr_vram_access_granted) gpu_is_last_owner <=  1'b0;

// Video RAM signal muxing
assign  vid_ram_sw_dout_o            = vid_ram_dout_i;

assign  vid_ram_gpu_dout_o           = vid_ram_dout_i;
assign  vid_ram_gpu_dout_rdy_nxt_o   = gpu_vram_access_granted;

assign  vid_ram_refr_dout_o          = vid_ram_dout_i;
assign  vid_ram_refr_dout_rdy_nxt_o  = refr_vram_access_granted;

assign  vid_ram_addr_o               = ({`VRAM_AWIDTH{ sw_vram_access_granted  }} & vid_ram_sw_addr_i  ) |
                                       ({`VRAM_AWIDTH{ gpu_vram_access_granted }} & vid_ram_gpu_addr_i ) |
                                       ({`VRAM_AWIDTH{ refr_vram_access_granted}} & vid_ram_refr_addr_i) ;

assign  vid_ram_din_o                = ({          16{ sw_vram_access_granted  }} & vid_ram_sw_din_i   ) |
                                       ({          16{ gpu_vram_access_granted }} & vid_ram_gpu_din_i  ) |
                                       ({          16{ refr_vram_access_granted}} & vid_ram_refr_din_i ) ;

assign  vid_ram_wen_o                = (              ~sw_vram_access_granted     | vid_ram_sw_wen_i   ) &
                                       (              ~gpu_vram_access_granted    | vid_ram_gpu_wen_i  ) &
                                       (              ~refr_vram_access_granted   | vid_ram_refr_wen_i ) ;

assign  vid_ram_cen_o                = vid_ram_sw_cen_i & vid_ram_gpu_cen_i & vid_ram_refr_cen_i;


endmodule // ogfx_ram_arbiter