// 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)

Reply via email to