// Copyright 2006, Traversal Technology
// For licensing, see Traversal Dual License
/* NOTES:
Regarding refresh: The entire memory controller is designed as a
synchronous machine that can't process any sort of potentially
out-of-band request such as refresh, or lmr. Rather, whatever
logic that is responsible for arbitrating between different memory
requests (video, drawing engine, etc.) is also responsible for
scheduling refreshes. That same arbiter should also manage lmr
commands (and the precharge-all that must preceed it), and host
software is therefore responsible for disabling refresh and video
before requesting pre-all and lmr commands (as well as the timing
required for those). With this design, memory refreshes can be
postponed in favor of video reads. As long as the average
refresh rate remains constant, there won't be any problems.
next_state_in: The second-to-last pipeline stage must anticipate
what state that the last stage will go into and whether or not it
should go busy on the first cycle of the given command. This
is simple enough for the cases of refresh, lmr, precharge_all, etc.
The tricky bit is read/write: If the row is open but is a miss,
then next_state_in should be set to s_precharge. If the row is closed,
then next_state_in should be set to s_activate.
Timing numbers are handled as shift registers. If you want a delay
of zero cycles, then the low bit of the wait period must be set to 1.
If you want a wait of 1 cycle, then bit zero is 0, and bit one is 1.
For each additional cycle of delay, shift one to the left.
Note: Timing numbers should have 1-bits all the way out to the left.
That is, for a delay of 2, must have 16'b1111111111111100. This is
important to prevent stalls. The time act2pre can be greater than
(act2rw + [rw]2pre), so in the logic, we use a bitwise AND to select
the longer of the remaining time periods to compute when precharge
is next allowed. Requiring the 1-bits allows the bitwise AND to
produce meaningful results. There aren't any failsafes built into the
logic. If this is programmed with bad timing numbers, and a shift
register is ever set entirely to zero, a hang is likely. Perhaps
we'll add a failsafe to deal with cosmic rays.
*/
// This module is the tail end of the control pipeline.
// It implements the state machine that manages bus cycles,
// delays, etc.
module memctl_fsm(
clock,
clock_90,
clock_rd,
reset,
// Timing delays
r2w_wait,
w2r_wait,
act2rw_wait,
w2pre_wait,
r2pre_wait,
pre2act_wait,
act2pre_wait,
ref2act_wait,
cas_latency,
// Command to process
cmd_in,
next_state_in,
bank_in,
row_in,
col_in,
wdata_in,
wbytes_in,
row_miss,
// Outputs
bus_cmd,
addr_out,
expect_read,
dq_out,
dq_en,
dm_out,
dqs_out,
dqs_en
);
input clock, clock_90, clock_rd;
input [7:0] r2w_wait, w2r_wait, act2rw_wait,
input [15:0] r2pre_wait, w2pre_wait;
input [15:0] pre2act_wait;
input [15:0] act2pre_wait, refresh2act_wait;
input [3:0] cas_latency;
input [1:0] cmd_in;
input [2:0] next_state_in;
input [1:0] bank_in;
input [12:0] row_in, col_in;
input [63:0] wdata_in;
input [7:0] wbytes_in;
input row_miss;
output [2:0] bus_cmd; // {RAS, CAS, WE}
output [12:0] addr_out;
output expect_read;
output [31:0] dq_out, dq_en;
output [7:0] dqs_out, dqs_en, dm_out;
// Commands accepted by this unit
parameter cmd_none = 0;
parameter cmd_read = 1;
parameter cmd_write = 2;
parameter cmd_refresh = 3;
// States directly accessibly by previous stage
parameter s_none = 0;
parameter s_precharge = 1;
parameter s_activate = 2;
parameter s_precharge_all = 3;
parameter s_refresh0 = 4;
parameter s_lmr = 5;
// Other states
parameter s_refresh1 = 6;
// Bus commands
parameter bc_none = 3'b111;
parameter bc_activate = 3'b011;
parameter bc_read = 3'b101;
parameter bc_write = 3'b100;
parameter bc_precharge = 3'b010;
parameter bc_refresh = 3'b001;
parameter bc_lmr = 3'b000;
reg [7:0] can_write, can_read;
reg [15:0] can_act;
reg [15:0] can_pre; // This one gets AND'ed
reg finished_last_command;
reg [2:0] state;
integer i;
// Compute busy signal (can we execute given command?)
always @(cmd_in or row_miss or finished_last_command) begin
case (cmd_in)
cmd_none: busy = 0;
cmd_read: busy = !can_read[0] || (row_miss &&
!finished_last_command);
cmd_write: busy = !can_write[0] || (row_miss &&
!finished_last_command);
cmd_refresh: busy = !finished_last_command;
endcase
end
reg [1:0] bank_hold;
reg [12:0] row_hold, col_hold;
reg [63:0] wdata_hold;
reg [7:0] wbytes_hold;
reg expect_write, expect_read;
always @(posedge clock or negedge reset) begin
if (!reset) begin
can_read <= 0;
can_write <= 0;
can_act <= 1;
can_pre <= 1;
state <= s_none;
finished_last_command <= 1;
expect_write <= 0;
expect_read <= 0;
end else begin
// Increment counters
for (i=0; i<7; i=i+1) begin
can_write[i] <= can_write[i+1] | can_write[i];
can_read[i] <= can_read[i+1] | can_read[i];
end
for (i=0; i<15; i=i+1) begin
can_act[i] <= can_act[i+1] | can_act[i];
can_pre[i] <= can_pre[i+1] | can_pre[i];
end
// can_pre[15] <= 1; failsafe?
// Commands are asserted for one cycle.
// On off cycles, command must be set to none.
bus_cmd <= bus_cmd_none;
// expect_write pulsed once per write
expect_write <= 0;
expect_read <= 0;
// Default outputs for most states, including don't-cares
addr_out <= col_hold;
bank_out <= bank_hold;
case (state)
// First step of refresh: precharge all banks
s_refresh0: begin
can_act <= pre2act_wait;
{can_read, can_write} <= 0;
// addr_out[10] <= 1; from prev stage
if (can_pre[0]) begin
state <= s_refresh;
bus_cmd <= bc_precharge;
end
end
// Manual precharge-all command
s_precharge_all: begin
can_act <= pre2act_wait;
{can_read, can_write} <= 0;
// addr_out[10] <= 1; from prev stage
if (can_pre[0]) begin
state <= s_none;
bus_cmd <= bc_precharge;
end
end
// Second step of refresh: send refresh command
s_refresh1: begin
// Optional, add if improves timing:
// {can_read, can_write} <= 0;
if (can_act[0]) begin
state <= s_none;
bus_cmd <= bc_refresh;
can_act <= refresh2act_wait;
finished_last_command <= 1;
end
end
// In case of row miss: precharge one bank
s_precharge: begin
can_act <= pre2act_wait;
{can_read, can_write} <= 0;
// addr_out[10] <= 0; from prev stage
// bank_out <= bank_hold; from above
if (can_pre[0]) begin
state <= s_activate;
bus_cmd <= bc_precharge;
end
end
// In case of closed row: activate one
s_activate: begin
can_read <= act2rw_wait;
can_write <= act2rw_wait;
can_pre <= act2pre_wait;
// bank_out <= bank_hold; from above
addr_out <= row_hold; // only place we use row
if (can_act[0]) begin
state <= s_none;
bus_cmd <= bc_activate;
finished_last_command <= 1;
end
end
// Manual LMR command
s_lmr: begin
{can_read, can_write} <= 0; // optional
// addr_out <= col_hold; from above
bus_cmd <= bus_cmd_lmr;
end
// Accept new command
s_none: begin
// Store everything that came with command
bank_hold <= bank_in;
row_hold <= row_in;
col_hold <= col_in;
wdata_hold <= wdata_in;
wbytes_hold <= wbytes_in;
if (!finished_last_command) state <= next_state_in;
addr_out <= col_in;
bank_out <= bank_in;
case (cmd_in)
cmd_read: begin
// Here's the bitwise AND where bad timing
// numbers can bite us.
can_pre <= can_pre & r2pre_wait;
can_write <= r2w_wait;
if (can_read[0]) begin
bus_cmd <= bc_read;
finished_last_command <= 0;
expect_read <= 1;
end
end
cmd_write: begin
// Here's the bitwise AND where bad timing
// numbers can bite us.
can_pre <= can_pre & w2pre_wait;
can_read <= w2r_wait;
if (can_write[0]) begin
bus_cmd <= bc_write;
finished_last_command <= 0;
expect_write <= 1;
end
end
default: begin
finished_last_command <= 0;
end
endcase
end
endcase
endcase
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)