I am considering the efficiency of an interpreter-based controller. 
>From the current thread and the DDR spec, we may want to be able to
issue read- or write-requests for every cycle. This is doable if the
'read' and 'write' instructions halt and wait for new requests rather
than exiting.  However, it may be difficult to avoid missing a cycle
when we need to precharge and the memory is ready for it.

OTOH, it seems very simple to code the time critical
read/write/precharge/active directly in hardware. Initialisation is more
complex, but we could still have that in "program" form where it is
simply an sequence of (ddr_signals, delay), no tests, no jump. Attached
is a sketch which implements read/write only, no init, no refresh, and
not debugged. If I haven't missed something essential, this doesn't seem
to be complex. Some of the parameters should be put into configurable
registers. Also, the interface may be a bit awkward as the client must
hold the input until it receives 'req_received', but at the same cycle
it can issue a new request. This can be fixed.

I assume the clock of the memory should be registered, as in the PROM
controller. But what about the double data rate? Do we have clock which
is pi/2 skewed, or does someone have black magic to teach? Also, it's
not clear to me what terminates a write burst. Is it a negative level of
the strobe where there should be of a rising edge?

`define COL_WIDTH 9
`define ROW_WIDTH 13
`define BANK_WIDTH 2
`define ADDR_WIDTH 24
`define BANK_COUNT (1 << `BANK_WIDTH)
`define COL_BITS  `COL_WIDTH              -1 : 0
`define ROW_BITS  `COL_WIDTH + `ROW_WIDTH -1 : `COL_WIDTH
`define BANK_BITS `ADDR_WIDTH             -1 : `COL_WIDTH + `ROW_WIDTH
`define BNR_BITS  `ADDR_WIDTH             -1 : `COL_WIDTH
`define ADDR_BITS `ADDR_WIDTH             -1 : 0

module ddr_ctl_np(clk, reset_, addr, req_accepted,
                  rd_req, rd_data, rd_valid,
                  wr_req, wr_data, wr_mask,
                  ddr_clk, ddr_clk_, ddr_cke, ddr_bank, ddr_addr,
                  ddr_cmd, ddr_dm, ddr_dq_io, ddr_dqs);
    input clk;
    input reset_;

    input[`ADDR_BITS] addr;     // address for read or write
    output       req_accepted;  // goes to 1 when current request is accepted

    input        wr_req;        // write request
    input[31:0]  wr_data;       // data to write
    input[3:0]   wr_mask;       // byte mask for wr_data

    input        rd_req;        // read request
    output[31:0] rd_data;       // data from a previous read
    output       rd_valid;      // rd_data from previous read is available

    // Connectors for two DDR memories. Some of the ports connects to both
    // memories, for others the lines are split between them.
    output ddr_clk, ddr_clk_, ddr_cke;
    output[`BANK_WIDTH-1:0] ddr_bank;
    output[11:0] ddr_addr;
    output[2:0] ddr_cmd; // = {ddr_ras_,ddr_cas_,ddr_we_}
    output[3:0] ddr_dm;
    inout[31:0] ddr_dq_io;
    inout[1:0] ddr_dqs;

reg req_accepted;

// DDR
reg[11:0] ddr_addr;
reg[31:0] ddr_dq;
reg ddr_cke;
reg[2:0] ddr_cmd;
parameter DDR_CMD_NOOP      = 3'b111;
parameter DDR_CMD_READ      = 3'b101;
parameter DDR_CMD_WRITE     = 3'b100;
parameter DDR_CMD_PRECHARGE = 3'b010;
parameter DDR_CMD_ACTIVE    = 3'b011;
reg[3:0] ddr_dm;
wire ddr_clk = clk;   // FIXME
wire ddr_clk_ = !clk; // FIXME
wire[31:0] ddr_dq_io;
assign ddr_dq_io = ddr_cmd == DDR_CMD_WRITE? ddr_dq : 32'bz;

wire[1:0] addr_bank = addr[`BANK_BITS];
wire[`BANK_WIDTH-1:0] ddr_bank = addr[`BANK_BITS];
reg have_open_row[0:`BANK_COUNT-1];
reg[`ROW_WIDTH-1:0] open_row[0:`BANK_COUNT-1];

reg[3:0] t_precharge;
reg[3:0] t_active;
reg[3:0] t_read;
reg[3:0] t_write;
parameter T_READ_TO_WRITE       = 3;  // = CL + data cycles
parameter T_WRITE_TO_READ       = 8;  // = tWTR + tDQSS + data cycles
parameter T_ACTIVE_TO_RW        = 15; // = tRCD
parameter T_WRITE_TO_PRECHARGE  = 15; // = tWR + some constant
parameter T_READ_TO_PRECHARGE   = 3;  // = CL + some constant
parameter T_PRECHARGE_TO_ACTIVE = 15; // = tRP
parameter T_ACTIVE_TO_PRECHARGE = 40; // = tRAS
parameter T_REFRESH_TO_ACTIVATE = 70; // = tRFC
parameter T_CAS = 3;

reg[3:0] rd_valid_queue;
assign rd_valid = rd_valid_queue[0];
assign rd_data = ddr_dq;

wire[`ROW_WIDTH-1:0] bank_open_row = open_row[addr_bank];
wire row_match = addr[`ROW_BITS] == bank_open_row;

integer i;

always @(posedge clk or negedge reset_) begin
    if (!reset_) begin
        t_precharge <= 0;
        t_active    <= 0;
        t_read      <= 0;
        t_write     <= 0;
        req_accepted <= 0;
        rd_valid_queue <= 4'0;
        ddr_dq <= 32'b0;
        ddr_dm <= 4'b0;
        ddr_addr <= 12'b0;
        for (i = 0; i < `BANK_COUNT; i = i + 1) begin
            have_open_row[i] <= 0;
            open_row[i] <= `ROW_WIDTH'b0;
        end
        ddr_cke <= 1'b1;
        ddr_cmd <= DDR_CMD_NOOP;
    end else begin
        // Counters.
        if (t_precharge) t_precharge <= t_precharge - 1;
        if (t_active)    t_active    <= t_active    - 1;
        if (t_read)      t_read      <= t_read      - 1;
        if (t_write)     t_write     <= t_write     - 1;
        rd_valid_queue <= rd_valid_queue >> 1;

        ddr_cmd <= DDR_CMD_NOOP;
        req_accepted <= 0;
        if (rd_req || wr_req) begin
            if (have_open_row[addr_bank]) begin
                if (row_match) begin
                    // Row hit, issue read or write if requested
                    if (wr_req) begin
                        if (t_write == 0) begin
                            if (t_precharge < T_WRITE_TO_PRECHARGE)
                                t_precharge <= T_WRITE_TO_PRECHARGE;
                            t_read <= T_WRITE_TO_READ;
                            rd_valid_queue
                                <= (rd_valid_queue >> 1) | (1 << T_CAS);
                            req_accepted <= 1;

                            ddr_cmd <= DDR_CMD_WRITE;
                            ddr_addr <= addr[`COL_BITS];
                            ddr_dq <= wr_data;
                            ddr_dm <= wr_mask;
                        end
                    end else if (rd_req) begin
                        if (t_read == 0) begin
                            if (t_precharge < T_READ_TO_PRECHARGE)
                                t_precharge <= T_READ_TO_PRECHARGE;
                            t_write <= T_READ_TO_WRITE;
                            req_accepted <= 1;

                            ddr_cmd <= DDR_CMD_READ;
                            ddr_addr <= addr[`COL_BITS];
                        end
                    end
                end else begin
                    // Row miss, precharge
                    if (t_precharge == 0) begin
                        t_active <= T_PRECHARGE_TO_ACTIVE;
                        have_open_row[ddr_bank] <= 0;

                        ddr_cmd <= DDR_CMD_PRECHARGE;
                        ddr_addr <= addr[`BANK_BITS];
                    end
                end
            end else begin
                // No row open, activate
                if (t_active == 0) begin
                    t_precharge <= T_ACTIVE_TO_PRECHARGE;
                    t_read      <= T_ACTIVE_TO_RW;
                    t_write     <= T_ACTIVE_TO_RW;
                    have_open_row[ddr_bank] <= 1;
                    open_row[ddr_bank] <= addr[`ROW_BITS];

                    ddr_addr <= addr[`ROW_BITS];
                    ddr_cmd <= DDR_CMD_ACTIVE;
                end
            end
        end
    end
end

endmodule
_______________________________________________
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