This is the memory access scheduler.  Some of you may notice that I
haven't yet taken into account certain suggestions.  For instance,
read counters are still external to the scheduler.  Those kinds of
optimizations will trickle in.  In the mean time, this is just a model
of how it should work (roughly).  Also, it's most certainly going to
need to be pipelined.

Please look this over.



// There will be four instances of this arbiter, one for each memory
// controller.


/*
Notes:
    It will become an issue later that there is no way to throttle
    the return data coming from the memory controller.  A variety
    of solutions exist, one of which is to count the outstanding
    requests for a given agent and never submit more than can be
    accepted by the return queue.

    This is not a problem for video, as its huge return queue will
    never fill.

Addresses:
    All addresses refer to 64-bit words


LMR:
    To load the mode register, first ensure that refresh is disabled.
    Check the spec for the DRAMs for required delays, which must be
    enforced by software.
    A precharge command must be issued (reg 2).
    Then LMR or LMR-extended (reg 0) can be issued.
*/

module arbiter(
    input clock,
    input reset_,

    // Config interface
    input reg_clock,
    input [2:0] reg_addr,
    input [31:0] reg_data,
    input reg_do_write,

    // Read end of request queue from bridge
    // This is only a 32-bit interface
    input [27:0] br_addr,
    input [63:0] br_wdata,
    input [7:0] br_bytes,
    input br_do_read,
    input br_do_write,
    input br_valid,
    output br_deq,

    // Write end of read return queue to bridge
    output [63:0] br_rdata,
    output br_rdata_enq,
    // input [3:0] br_rdata_free,

    // Read end of video request queue (head0)
    input [27:0] vid0_addr,
    input vid0_valid,
    output vid0_deq,

    // Read end of video request queue (head0)
    input [27:0] vid1_addr,
    input vid1_valid,
    output vid1_deq,

    // Write end of read return queue to video
    output [63:0] vid0_rdata,
    output vid0_rdata_enq,

    // Write end of read return queue to video
    output [63:0] vid1_rdata,
    output vid1_rdata_enq,

    // Memory controller
    output reg [2:0] cmd_mem,
    output reg [1:0] bank_mem,
    output reg [12:0] row_mem,
    output reg [12:0] col_mem,
    output reg [63:0] wdata_mem,
    output reg [7:0] wbytes_mem,
    output reg [2:0] rtag_mem,
    input busy_mem,

    input [63:0] rdata_mem,
    input rdata_valid_mem,
    input [2:0] rtag_return
);



// Config regs and load mode register (lmr)
reg [14:0] lmr_data;
reg [3:0] lmr_delay;
reg lmr_addr;
reg [10:0] refresh_delay;
reg refresh_enable;

always @(posedge reg_clock or negedge reset_) begin
    if (!reset_) begin
        lmr_write[0] <= 0;
        lmr_write[1] <= 0;
        lmr_data <= 0;
    end else begin
        lmr_write[0] <= lmr_write[0] >> 1;
        lmr_write[1] <= lmr_write[1] >> 1;
        if (reg_do_write) begin
            case (reg_addr[1:0])
                0: begin
                    lmr_data <= reg_data;
                    lmr_delay <= 4'b1110;
                    lmr_addr <= 0;
                end
                2: begin
                    lmr_addr <= 1;
                    lmr_delay <= 4'b1110;
                end
                3: {refresh_enable, refresh_delay} <= reg_data;
            endcase
        end
    end
end


reg last_lmr_delay;
always @(posedge clock) begin
    last_lmr_delay <= lmr_delay[0];
    do_lmr <= !last_lmr_delay && lmr_delay[0];
end




// Refresh counter
integer i;
reg [10:0] refresh_counter;
reg [11:0] borrow, zero;
always @(posedge clock or negedge reset_) begin
   if (!reset_) begin
       borrow <= 1;
       zero <= 1;
       refresh_counter <= 0;
   end else begin
       if (zero[11]) begin
           refresh_counter <= refresh_delay;
           zero <= 1;
           borrow <= 1;
       end else begin
           for (i=0; i<11; i=i+1) begin
               zero[i+1] <= zero[i] && !refresh_counter[i];
               borrow[i+1] <= !refresh_counter[i] && borrow[i];
               refresh_counter[i] <= refresh_counter[i] ^ borrow[i];
           end
       end
   end
end

reg refresh_pending;
wire refresh_deq;
always @(posedge clock or negedge reset_) begin
    if (!reset_) begin
        refresh_pending <= 0;
    end else begin
        if (zero[11] && refresh_enable) refresh_pending <= 1;
        if (refresh_deq) refresh_pending <= 0;
    end
end



parameter mem_read = 3'd1;
parameter mem_write = 3'd2;
parameter mem_precharge = 3'd3;
parameter mem_refresh = 3'd4;
parameter mem_lmr = 3'd5;



wire pci_access = br_valid;
wire vid0_access = vid0_valid;
wire vid1_access = vid1_valid;
wire refresh_access = refresh_pending;

// Scheduler
reg allow_pci, allow_vid0, allow_vid1, allow_refresh;
always @(posedge clock or negedge reset_) begin
    if (!reset_) begin
        allow_pci <= 0;
        allow_vid0 <= 0;
        allow_vid1 <= 0;
        allow_refresh <= 0;
    end else begin
        if (refresh_access) begin
            allow_refresh <= 1;
            allow_pci <= 0;
            allow_vid0 <= 0;
            allow_vid1 <= 0;
        end else if (vid0_access) begin
            allow_vid0 <= 1;
            allow_pci <= 0;
            allow_refresh <= 0;
            allow_vid1 <= 0;
        end else if (vid1_access) begin
            allow_vid0 <= 1;
            allow_pci <= 0;
            allow_refresh <= 0;
            allow_vid1 <= 1;
        end else if (pci_access) begin
            allow_pci <= 1;
            allow_vid0 <= 0;
            allow_refresh <= 0;
            allow_vid1 <= 0;
        end
    end
end


assign br_deq = allow_pci && !memctl_busy;
assign vid_deq = allow_video && !memctl_busy;
assign refresh_deq = allow_refresh && !memctl_busy;


// We will definitely need a stage of pipelining added here

wire lmr_cmd = do_lmr && !lmr_addr;
wire precharge_cmd = do_lmr && lmr_addr;
wire refresh_cmd = allow_refresh;
wire pci_read_cmd = allow_pci && br_do_read && br_valid;
wire pci_write_cmd = allow_pci && br_do_write && br_valid;
wire vid0_cmd = allow_vid0 && vid0_valid;
wire vid1_cmd = allow_vid1 && vid1_valid;

assign cmd_mem =
    ({3{lmr_cmd}} & mem_lmr) |
    ({3{precharge_cmd}} & mem_precharge) |
    ({3{refresh_cmd}} & mem_refresh) |
    ({3{pci_read_cmd}} & mem_read) |
    ({3{pci_write_cmd}} & mem_write) |
    ({3{vid0_cmd}} & mem_read) |
    ({3{vid1_cmd}} & mem_read);

assign bank_mem =
    ({2{lmr_cmd}} & lmr_data[14:13]) |
    ({2{pci_read_cmd}} & br_addr[22:21]) |
    ({2{pci_write_cmd}} & br_addr[22:21]) |
    ({2{vid0_cmd}} & vid0_addr[22:21]) |
    ({2{vid1_cmd}} & vid1_addr[22:21]);

// 13-bit row
assign row_mem =
    ({13{pci_read_cmd}} & br_addr[20:8]) |
    ({13{pci_write_cmd}} & br_addr[20:8]) |
    ({13{vid0_cmd}} & vid0_addr[20:8]) |
    ({13{vid1_cmd}} & vid0_addr[20:8]);

// 16-bit 256Mb chips have 9-bit column; we use only even numbers
// Must make these configurable for ASIC
assign col_mem[8:1] =
    ({8{lmr_cmd}} & lmr_data[8:1]) |
    ({8{pci_read_cmd}} & br_addr[7:0]) |
    ({8{pci_write_cmd}} & br_addr[7:0]) |
    ({8{vid0_cmd}} & vid0_addr[7:0]) |
    ({8{vid1_cmd}} & vid0_addr[7:0]);
assign col_mem[0] = lmr_cmd & lmr_data[0];

assign wdata_mem = br_wdata;
assign wbytes_mem = br_bytes;

assign rtag_mem = {pci_read_cmd, vid0_cmd, vid1_cmd};


// Return path for read data
assign br_rdata = rdata_mem;
assign vid0_rdata = rdata_mem;
assign vid1_rdata = rdata_mem;
assign br_rdata_enq = rdata_valid_mem & rtag_return[2];
assign vid0_rdata_enq = rdata_valid_mem & rtag_return[1];
assign vid1_rdata_enq = rdata_valid_mem & rtag_return[0];


endmodule


-- 
Timothy Normand Miller
http://www.cse.ohio-state.edu/~millerti
Open Graphics Project
_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

Reply via email to