I recently revisited SDRAM programming and wrote down some of my experiences.[Copy link]
Before, someone said that if you can write SDRAM alone, it can be basically determined that FPGA programming is already at the elementary level. I won't discuss whether this is right or not, but I always felt that it was difficult to drive SDRAM with FPGA, and later I didn't insist and put it aside for a long time. Recently, I saw the AX309 board from a friend (I'm not advertising, if you are more concerned, search it yourself at night) and the relevant history will involve this thing, so I want to refer to other people's history and write my own experience. If there is anything wrong, please correct me. SDRAM data manuals, which can be found online, are provided by Micron and two Korean companies. The materials provided by the Koreans are very simple, and I don't have time to go to their official website or other channels to obtain more detailed information. So I looked at Micron's chips, because their datasheets are relatively complete. SDRAM is generally divided into two stages, the initialization stage and the normal working stage. Let's talk about the initialization phase first. The information found on the Internet shows that the initialization process is as follows: According to the initialization timing diagram provided by Micron, there are two differences from the above figure: 1. The input stable period is 100us; 2. The third state is 2 cycles. The details are as follows: First, take the timing diagram of Micron as the standard. SDRAM has a clock range requirement. The external clock is 50M. Through the internal frequency multiplication of FPGA, it can output a 100Mhz clock. One clock cycle is 10ns. When looking at this timing diagram, there are 3 points that should be noted: 1. What is the state process like. All the books about Verilog that I have seen will talk about the state machine, which is equivalent to the backbone of the design, at least in the synthesizable design. 2. The change of the corresponding control word, such as CMD, DQM, ADDR, BA. What is its value in each state without clock cycle. 3. The value of DQ. The general design implements the above 3 points in 3 files, which will involve the problem of common parameters. In order to solve this problem, the same parameters used in multiple files can be written in one file, and then this file is called through `include. For example: parameter TRP_CLK = 9'd4,//1, //TRP=18ns precharge effective period TRFC_CLK = 9'd6,//3, //TRC=60ns automatic pre-refresh period TMRD_CLK = 9'd6,//2, //Mode register setting waiting clock period TRCD_CLK = 9'd2,//1, //TRCD=18ns row strobe period The following will be continued when the writing is almost done!
I have been busy these past two days and have been a little delayed. Let me first talk about the setting of the input stabilization period during initialization. Two always are used, one is the system state machine and the other is the counter. When enabled, the counter starts counting, and when it counts to 10000-1, the state machine switches from the input stable state to the pre-charge state. During this process, neither the control signal nor the data is active.
From the timing diagram, the initialization is divided into power-on waiting, pre-charging, pre-charging waiting, first self-refresh, first self-refresh waiting, second self-refresh, second self-refresh waiting, and register setting. During initialization, a clock counter is set. When the self-refresh is enabled, it starts counting, and after reaching the counting state, it is maintained. The code for the initialization state is shown below. The way I write this code may be different from the general one, but I have no problem writing it this way for simulation. In addition, this is the recommended writing method for a development board that I bought online before, and I am more accepting of it now. I heard that this is the prescribed writing method of a certain company. Everyone has their own opinions. always@(posedge SYSCLK or negedge RST_N) begin if(!RST_N) INIT_CNT <= 15'd0; else INIT_CNT <= INIT_CNT_F; end always@(*) begin if(INIT_EN == 1'b0) //Or the system starts counting automatically upon power-on INIT_CNT_F = 15'd0; else if(INI_CNT == 15'd19999) INIT_CNT_F = INIT_CNT; else INIT_CNT_F = INIT_CNT + 15'd1; end Then we need a clock counter. Because we can see in the timing diagram that the state is switched according to the clock count. always@(posedge SYSCLK or negedge RST_N) begin if(!RST_N) CLK_CNT <= 8'd0; //The specific bit width of the data is adjusted according to the progress else CLK_CNT <= CLK_CNT_F; end always@(*) begin if(INIT_CS != INIT_NS) //INIT_CS is the current state, INIT_NS is the next state CLK_CNT_F = 8'd0; else CLK_CNT_F = CLK_CNT + 8'd1; end Let's take a look at the implementation of the state machine: always @ (*)begin case (INIT_CS) `I_NOP: INIT_NS <= done_200us ? `I_PRE:`I_NOP; //After 200us after power-on reset, enter the next state `I_PRE: INIT_NS <= `I_TRP; //Precharge state `I_TRP: INIT_NS <= (`end_trp) ? `I_AR1:`I_TRP; //Precharge wait TRP_CLK clock cycles `I_AR1: INIT_NS <= `I_TRF1; //1st self-refresh `I_TRF1: INIT_NS <= (`end_trfc) ? `I_AR2:`I_TRF1; //Wait for the first self-refresh to end, TRFC_CLK clock cycles `I_AR2: INIT_NS <= `I_TRF2; //2nd self-refresh `I_TRF2: INIT_NS <= (`end_trfc) ? `I_MRS:`I_TRF2; //Wait for the second self-refresh to end, TRFC_CLK clock cycles `I_MRS: INIT_NS <= `I_TMRD;//Mode register setting (MRS) `I_TMRD: INIT_NS <= (`end_tmrd) ? `I_DONE:`I_TMRD; //Wait for the mode register to be set, TMRD_CLK clock cycles `I_DONE: INIT_NS <= `I_DONE; //SDRAM initialization setting completion flag default: INIT_NS <= `I_NOP; endcase end Among them, end_trp, end_trfc, and end_tmrd are the separately defined clock count parameters mentioned above. For example, it can be expressed as follows: `define end_trp = 3;