片上系统设计思想与源代码分析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

5.3 SDRAM控制器源代码分析

5.3.1 SDRAM控制器源代码列表

控制器源代码文件列表如下:

5.3.2 SDRAM初始化和WISHBONE从设备接口

sdr_wb_if.v完成SDRAM初始化和WISHBONE从设备接口,该设计的状态机方框图如图5-9所示。

图5-9 SDRAM初始化状态机

其设计源代码如下:

//chenxi01@ict.ac.cn
`include "timescale.v"
module sdr_wb_if(  clk_i,  rst_i,  cyc_i,  stb_i,  dat_i,  dat_o,  adr_i,  we_i,  ack_o,
sel_i,  istate,  cstate,  sys_addr,  sdr_dq,  sdr_dqm);
`include "sdr_defines.v"
……//此处忽略输入输出信号
wire        sys_init_done;  //表示SDRAM初始化结束
reg         sys_ref_req;
reg [3:0]   istate;      // 初始化状态机状态
reg [3:0]   cstate;      //命令状态机状态
reg [3:0]   clkCNT;
reg        syncResetClkCNT; // reset clkCNT to 0
reg        drive_data;
reg [31:0] sdr_dq_out;
assign sdr_dq= drive_data?sdr_dq_out:32'bzzzz_zzzz_zzzz_zzzz_zzzz_zzzz_zzzz_zzzz;
//---------------------------------------------------------------------
// local definitions
`define endOf_tRP        clkCNT == NUM_CLK_tRP
`define endOf_tRFC       clkCNT == NUM_CLK_tRFC
`define endOf_tMRD       clkCNT == NUM_CLK_tMRD
`define endOf_tRCD       clkCNT == NUM_CLK_tRCD
`define endOf_Cas_Latency  clkCNT == NUM_CLK_CL
`define endOf_Read_Burst  clkCNT == NUM_CLK_READ -1
`define endOf_Write_Burst  clkCNT == NUM_CLK_WRITE
`define endOf_tDAL       clkCNT == NUM_CLK_WAIT
//初始化状态机
reg      sys_delay_200us;
reg [14:0] sys_delay_cnt;
always @(posedge clk_i or posedge rst_i) begin
    if(rst_i) begin sys_delay_200us<=0; sys_delay_cnt<=0; end
    else begin
      if(sys_delay_cnt<15'h7fff) sys_delay_cnt<=sys_delay_cnt+1;
      else sys_delay_200us<=1;
    end
end
reg second_set;
always @(posedge clk_i or posedge rst_i)
  if (rst_i) begin    istate <= #tDLY i_NOP;  second_set<=0; end
else case (istate)
    i_NOP:  // 等待200us
            if (sys_delay_200us) istate <= #tDLY i_PRE;
    i_PRE:  //预充电,即PPrecharge all
            istate <= #tDLY (NUM_CLK_tRP == 0) ? i_AR1 : i_tRP;
    i_tRP:  //等待tRP
            if (`endOf_tRP) istate <= #tDLY i_AR1;
    i_AR1:  // Auto Referesh
            istate <= #tDLY (NUM_CLK_tRFC == 0) ? i_AR2 : i_tRFC1;
    i_tRFC1: // 等待tRFC
            if (`endOf_tRFC) istate <= #tDLY i_AR2;
    i_AR2:  // Auto Referesh
            istate <= #tDLY (NUM_CLK_tRFC == 0) ? i_MRS : i_tRFC2;
    i_tRFC2: //等待tRFC
            if (`endOf_tRFC) istate <= #tDLY i_MRS;
    i_MRS:  // Load Mode Register
            istate <= #tDLY (NUM_CLK_tMRD == 0) ? i_ready : i_tMRD;
    i_tMRD:  //等待tMRD
            if (`endOf_tMRD) begin
                if(second_set) istate <= #tDLY i_ready;
              else begin second_set<=1;  istate <= #tDLY i_NOP; end
              end
    i_ready: // stay at this state for normal operation
            istate <= #tDLY i_ready;
    default: istate <= #tDLY i_NOP;
    endcase
//命令状态机State Machine
assign sys_init_done=istate==i_ready;
reg we_i_reg;
always @(posedge clk_i or posedge rst_i)
  if (rst_i) begin
    cstate <= #tDLY c_idle;    sdr_dq_out<=#tDLY 0;
    sdr_dqm<=#tDLY 4'b0;    ack_o<=#tDLY 1'b0;
    drive_data<=#tDLY 1'b0;    sys_addr<=#tDLY 1'b0;
    dat_o<=#tDLY 1'b0;    we_i_reg<=#tDLY 1'b0;
     sdr_dqm<=#tDLY 4'b1111;
  end else
   case (cstate)
     c_idle: begin  // wait until refresh request or addr strobe asserted
            if (sys_ref_req && sys_init_done) cstate <= #tDLY c_AR;
            else if (cyc_i&stb_i&sys_init_done&(!ack_o)) begin
                cstate <= #tDLY c_ACTIVE;
            end
            sys_addr<=#tDLY adr_i[23:2];  we_i_reg<=#tDLY we_i;
            if (cyc_i&stb_i&sys_init_done&we_i&(!ack_o)) begin
                ack_o<=#tDLY 1'b1;  sdr_dq_out<=#tDLY dat_i;
                sdr_dqm<=#tDLY ~sel_i;              end
            else begin
                ack_o<=#tDLY 1'b0;
                if(sys_init_done) sdr_dqm<=#tDLY 4'b0;
                else sdr_dqm<=#tDLY 4'b1111;
            end
            drive_data<=#tDLY 1'b0;
          end
     c_ACTIVE: begin // assert row/Bank addr
            ack_o<=1'b0;
                drive_data<=#tDLY we_i_reg;
            if (NUM_CLK_tRCD == 0)
                cstate <= #tDLY (we_i_reg) ? c_WRITEA:c_READA;
            else cstate <= #tDLY c_tRCD;
        end
    c_tRCD:  // wait until tRCD satisfied
            if (`endOf_tRCD)
              cstate <= #tDLY (we_i_reg) ?  c_WRITEA:c_READA;
    c_READA:  // assert col/Bank addr for read with auto-PPrecharge
            cstate <= #tDLY c_cl;
    c_cl:    // CASn latency
            if (`endOf_Cas_Latency) cstate <= #tDLY c_rdata;
    c_rdata:  begin // read cycle data phase
            ack_o<=1'b1;
            dat_o<=sdr_dq;
            if (`endOf_Read_Burst) cstate <= #tDLY c_idle;
        end
    c_WRITEA: begin // assert col/Bank addr for write with auto-PPrecharge
            cstate <= #tDLY c_wdata;
        end
    c_wdata:  // write cycle data phase
            if (`endOf_Write_Burst) cstate <= #tDLY c_tDAL;
    c_tDAL:  // wait until (tWR + tRP) satisfied before issuing next
            // SDRAM ACTIVE command
            if (`endOf_tDAL) begin
              cstate <= #tDLY c_idle;
              drive_data<=1'b0;
                end
    c_AR:    // auto-refresh
            cstate <= #tDLY (NUM_CLK_tRFC == 0) ? c_idle : c_tRFC;
    c_tRFC:  // wait until tRFC satisfied
            if (`endOf_tRFC) cstate <= #tDLY c_idle;
    default:
            cstate <= #tDLY c_idle;
  endcase
// sys_ref_req generation
parameter REFRESH_CNT_MAX=10'd600;
reg [9:0] refresh_cnt;
always @(posedge clk_i or posedge rst_i) begin
if (rst_i)  begin    refresh_cnt <= #tDLY 0;  end
else begin
   if(sys_init_done)
   begin
      if(refresh_cnt<=REFRESH_CNT_MAX)  refresh_cnt<=#tDLY refresh_cnt+1;
      else  refresh_cnt<=#tDLY 0;
   end
   else refresh_cnt <= #tDLY 0;
end
end
always @(posedge clk_i or posedge rst_i) begin
if (rst_i)  begin    sys_ref_req <= #tDLY 0;  end
else begin
   if(refresh_cnt==REFRESH_CNT_MAX)
   begin sys_ref_req <= #tDLY 1; end
   else if (cstate == c_AR)  sys_ref_req <= #tDLY 0;
end
end
// Clock Counter
always @(posedge clk_i)
if (syncResetClkCNT) clkCNT <= #tDLY 0;
else clkCNT <= #tDLY clkCNT + 1;
// syncResetClkCNT generation
always @(istate or cstate or clkCNT)
case (istate)
  i_PRE:       syncResetClkCNT <= #tDLY (NUM_CLK_tRP == 0) ? 1 : 0;
  i_AR1, i_AR2:  syncResetClkCNT <= #tDLY (NUM_CLK_tRFC == 0) ? 1 : 0;
  i_NOP:      syncResetClkCNT <= #tDLY 1;
  i_tRP:       syncResetClkCNT <= #tDLY (`endOf_tRP) ? 1 : 0;
  i_tMRD:      syncResetClkCNT <= #tDLY (`endOf_tMRD) ? 1 : 0;
  i_tRFC1, i_tRFC2:  syncResetClkCNT <= #tDLY (`endOf_tRFC) ? 1 : 0;
  i_ready:
      case (cstate)
        c_ACTIVE: syncResetClkCNT <= #tDLY (NUM_CLK_tRCD == 0) ? 1 : 0;
        c_idle: syncResetClkCNT <= #tDLY 1;
        c_tRCD: syncResetClkCNT <= #tDLY (`endOf_tRCD) ? 1 : 0;
        c_tRFC: syncResetClkCNT <= #tDLY (`endOf_tRFC) ? 1 : 0;
        c_cl: syncResetClkCNT <= #tDLY (`endOf_Cas_Latency) ? 1 : 0;
        c_rdata:syncResetClkCNT <= #tDLY (clkCNT == NUM_CLK_READ) ? 1 : 0;
        c_wdata: syncResetClkCNT <= #tDLY (`endOf_Write_Burst) ? 1 : 0;
        default: syncResetClkCNT <= #tDLY 0;
      endcase
  default:  syncResetClkCNT <= #tDLY 0;
endcase
endmodule

5.3.3 SDRAM操作主状态机

sdr_sig.v实现了SDRAM操作主状态机,其状态转移如图5-10所示。

图5-10 SDRAM控制器主状态机

其设计源代码如下:

//chenxi01@ict.ac.cn
`include "timescale.v"
`define sdr_COMMAND  {sdr_csn, sdr_rasn, sdr_casn, sdr_wen}
module sdr_sig(clk_i, rst_i, sys_addr, istate, cstate, sdr_cke, sdr_csn, sdr_rasn,
sdr_casn, sdr_wen, sdr_ba, sdr_addr);
`include "sdr_defines.v"
……//忽略输入输出信号
// SDR SDRAM Control Singals
always @(posedge clk_i or posedge rst_i)
  if (rst_i) begin
    `sdr_COMMAND <= #tDLY INHIBIT;
    sdr_cke <= #tDLY 0; sdr_ba<= #tDLY 2'b11;sdr_addr<= #tDLY 12'b1111_1111_1111;
  end else
    case (istate)
    i_tRP, i_tRFC1, i_tRFC2, i_tMRD, i_NOP: begin
            `sdr_COMMAND <= #tDLY NOP;
            sdr_cke <= #tDLY 1; sdr_ba  <= #tDLY 2'b11;
            sdr_addr  <= #tDLY 12'b1111_1111_1111;
          end
    i_PRE: begin
            `sdr_COMMAND <= #tDLY PPRECHARGE;
            sdr_cke <= #tDLY 1; sdr_ba  <= #tDLY 2'b00;
            sdr_addr  <= #tDLY 12'b1111_1111_1111;
          end
    i_AR1,
    i_AR2: begin
            `sdr_COMMAND <= #tDLY AUTO_REFRESH;
            sdr_cke <= #tDLY 1;  sdr_ba  <= #tDLY 2'b11;
            sdr_addr  <= #tDLY 12'b1111_1111_1111;
          end
    i_MRS: begin
            `sdr_COMMAND <= #tDLY LOAD_MODE_REGISTER;
            sdr_cke <= #tDLY 1; sdr_ba  <= #tDLY 2'b00;
            sdr_addr  <= #tDLY {2'b00, MR_Write_Burst_Mode,
                      MR_Operation_Mode,MR_CAS_Latency,
                      MR_Burst_Type, MR_Burst_Length};
          end
    i_ready:
          case (cstate)
            c_idle, c_tRCD, c_tRFC, c_cl, c_rdata, c_wdata:
begin
                    `sdr_COMMAND <= #tDLY NOP;
                    sdr_cke <= #tDLY 1; sdr_ba  <= #tDLY 2'b11;
                    sdr_addr  <= #tDLY 12'b1111_1111_1111;
                    end
            c_ACTIVE: begin
                    `sdr_COMMAND <= #tDLY ACTIVE;
                    sdr_cke <= #tDLY 1;
                    sdr_ba  <= #tDLY sys_addr[BA_MSB:BA_LSB];//Bank
                    sdr_addr  <= #tDLY sys_addr[RA_MSB:RA_LSB];//row
                    end
            c_READA:  begin
                    `sdr_COMMAND <= #tDLY READ;
                    sdr_cke <= #tDLY 1;
                    sdr_ba  <= #tDLY sys_addr[BA_MSB:BA_LSB];//Bank
                    sdr_addr  <= #tDLY {
                              4'b0100, //enable auto PPrecharge
                              sys_addr[CA_MSB:CA_LSB]//column
                              };
                    end
          c_WRITEA: begin
                    `sdr_COMMAND <= #tDLY WRITE;
                    sdr_cke <= #tDLY 1;
                    sdr_ba  <= #tDLY sys_addr[BA_MSB:BA_LSB];//Bank
                    sdr_addr  <= #tDLY {
                              4'b0100, //enable auto PPrecharge,A10
                              sys_addr[CA_MSB:CA_LSB]//column
                            };
                  end
          c_AR:    begin
                    `sdr_COMMAND <= #tDLY AUTO_REFRESH;
                    sdr_cke <= #tDLY 1;
                    sdr_ba  <= #tDLY 2'b11;
                    sdr_addr  <= #tDLY 12'b1111_1111_1111;
                  end
          default:  begin
                    `sdr_COMMAND <= #tDLY NOP;
                    sdr_cke <= #tDLY 1; sdr_ba  <= #tDLY 2'b11;
                    sdr_addr  <= #tDLY 12'b1111_1111_1111;
                  end
        endcase
    default:
        begin
          `sdr_COMMAND <= #tDLY NOP;
          sdr_cke <= #tDLY 1; sdr_ba  <= #tDLY 2'b11;
          sdr_addr  <= #tDLY 12'b1111_1111_1111;
        end
  endcase
endmodule

5.3.4 SDRAM控制器顶层模块

sdr_sdramc_top定义了SDRAM控制器顶层模块,将SDRAM控制器的各个模块连接在一起。其源代码如下所示。

`include "timescale.v"
//`define SIMULATION
module
sdr_sdramc_top(clk_i,rst_i,we_i,data_i,data_o,addr_i,cyc_i,stb_i,ack_o,rty_o,err_o,
    sel_i,sdr_addr,sdr_ba,sdr_wen,sdr_csn,sdr_cke,sdr_rasn,sdr_casn,sdr_dq,sdr_dqm);
`include      "sdr_defines.v"
……//忽略输入输出信号
`ifdef SIMULATION
  assign ack_o=cyc_i&stb_i;
  assign rty_o=0; assign err_o=0;
  reg [31:0]data_o;
always @(posedge clk_i or posedge rst_i)
  if(rst_i) begin  data_o=0;  end
  else begin
      if(we_i& ack_o & data_i[23:0]!=addr_i) $stop;
      if((!we_i)& ack_o ) begin data_o[23:0]=addr_i;  end
  end
`else
wire [21:0] sys_addr;
wire [3:0]            istate;
wire [3:0]            cstate;
sdr_wb_if
sdr_wb_if(.clk_i(clk_i),.rst_i(rst_i),.cyc_i(cyc_i),.stb_i(stb_i),.dat_i(data_i),
.dat_o(data_o),.adr_i(addr_i),.we_i(we_i),.ack_o(ack_o),.sel_i(sel_i),.istate(istate
),.cstate(cstate),.sys_addr(sys_addr),.sdr_dq(sdr_dq),.sdr_dqm(sdr_dqm));
sdr_sig sdr_sig(.clk_i(clk_i),.rst_i(rst_i),.sys_addr(sys_addr),.istate(istate),
.cstate(cstate),.sdr_cke(sdr_cke),.sdr_csn(sdr_csn),.sdr_rasn(sdr_rasn),.sdr_casn(sd
r_casn),. sdr_wen(sdr_wen),.sdr_ba(sdr_ba),.sdr_addr(sdr_addr));
`endif
endmodule