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)