1. Read operation
SDRAM provides a variety of data reading methods, such as single reading, page burst reading (burst length 1, 2, 4, 8, full page), etc. At present, this paper only explains and simulates the full page burst mode, and provides explanations and simulations of other writing methods as appropriate.
2. Full page burst read operation
It should be noted that most SDRAM does not support automatic precharge in case of full page burst, so our read operation sequence includes manual precharge. The sequence diagram is as follows:
Each signal is described as follows:
CK: working clock. The specific clock frequency varies according to different chips
CKE: clock enable, which needs to be pulled up during the whole burst reading process (in fact, all other operations need to be pulled up except pulling down in sleep mode)
COMMAND: SDRAM commands are spliced by four lines, namely CS # (chip selection signal), RAS # (row gating signal), CAS # (column gating signal) and WE # (write enable signal). Through these four COMMAND lines, combined with SDRAM address, input and output data, various COMMAND operations can be carried out on SDRAM
DQM/DQML,DQMU: data mask, which can "bury" a bit of input or output data, that is, invalidate a bit
A[9:0],A[12:11]: data address line, which can also be used to set the mode register
A10: data address line, which can also be used to enable some specific operations, such as controlling the number of automatic precharge enabling and enabling precharge bank s
BA[1:0]: bank address
DQ data: data bus. In the process of burst reading, it is necessary to obtain the data output by SDRAM on the data bus according to the timing
Through the analysis of the sequence diagram, the initialization process can be summarized as follows:
1. Send the activation instruction, output the bank address of the data to be written on the bank bus, and output the row address of the data to be written on the address bus, so this operation is also called "row activation"
2. After sending the activation command, you need to wait for a certain time, that is, tRCD. During this period, you also need to send the NOP null command (the null command is sent to prevent misoperation of SDRAM)
3. After waiting, send the read instruction, output the column head address of the data to be read on the address bus, and then wait for the latency (CL, CAS latency) clock cycle
4. After the incubation period, the data bus starts to output data, and then data is output in each cycle until the burst termination instruction is sent to SDRAM to end the burst reading, so as to realize the control of burst length (it can not exceed the maximum number of one line of data, i.e. 2 * 9 = 512 data)
6. Send a precharge command to precharge all banks. Pulling A10 high means that all banks are selected
7. After the precharge operation, you need to wait for a certain time, that is, tRP. During this period, you also need to send NOP null command (the null command is sent to prevent misoperation of SDRAM)
8. After the Trp time, a burst write operation is completed
3. State machine
According to the sequence diagram of burst reading, it is not difficult to draw the following state machine:
Describe each state, state jump condition and output:
- RD_IDLE: In the initial state, when the initialization completion signal is pulled high (initialization is completed) and the arbitration module sends a burst read enable signal, it jumps to the next state RD_ACT, send NOP command in this state
- RD_ACT: The row is ACTIVE, only one clock cycle is maintained, and then jump to the next state RD_TRCD, send line activation instruction (ACTIVE) + bank address + line address
- RD_TRCD: line activation waiting state. After the waiting time in this state meets tRCD, it will jump to the next state RD_WR, send NOP command in this state
- RD_WR: Column READ / write state, only maintain one clock cycle, and then jump to the next state RD_CL, send READ instruction (READ) + bank address + column head address
- RD_CL: Latency waiting state. After the waiting time in this state meets Cl, it jumps to the next state RD_DATA, send NOP instruction in this state
- RD_DATA: data reading status. In this status, send NOP instruction first and register the data on the data bus at the same time; When all the data to be read is output (CL clock cycles in advance, because the data reading will lag CL clock cycles), send a burst termination instruction (BURST TERM), terminate the read operation and jump to RD_PRE status
- RD_PRE: Send the PRECHARGE command state, maintain only one clock cycle, and then jump to the next state RD_TRP, send line PRECHARGE command (PRECHARGE) + A10 pull up
- RD_TRP: Precharge command waiting state. After the waiting time in this state meets Trp, jump to the next state RD_END, send NOP command in this state
- RD_END: this status raises the burst write flag completion signal wr_end one cycle, tell the arbitration module that the write operation is completed, so that the arbitration module can arbitrate the refresh, write, read and other requests (to prevent timing conflicts)
4. Interface definition and overall design
The overall block diagram, input and output signals of the burst read module written by Verilog are as follows:
The signal description is as follows:
Signal name | Bit width | attribute | describe |
rd_clk | 1 | input | Clock signal, 100M |
rd_rst_n | 1 | input | Reset signal, active at low level |
init_end | 1 | input | Initialization completion signal. Read operation can be performed only after initialization is completed |
rd_en | 1 | input | Read enable signal, which can be read only after the signal is pulled high |
rd_addr | 24 | input | Some addresses of read operation are composed of bank address (2 bits), row address (13 bits) and column address (9 bits) |
rd_data | 16 | input | Data read from SDRAM |
rd_burst_len | 10 | input | Length of a burst read |
rd_ack | 1 | output | Read operation response signal, indicating that the module has read SDRAM |
rd_end | 1 | output | The read operation end signal only maintains one clock cycle after the end of the read operation |
rd_sdram_cmd | 4 | output | 13 bit SDRAM address |
rd_sdram_bank | 2 | output | 4-bit SDRAM command, consisting of {CS#,RAS#,CAS#,WE #} |
rd_sdram_addr | 13 | output | 2-digit BANK address, 4 banks in total |
rd_sdram_data | 16 | output | Data read from SDRAM output to arbitration module |
5. Verilog code
According to the above state machine description and overall design, it is not difficult to write Verilog code for module implementation (the comments are very detailed):
`timescale 1ns/1ns module sdram_read ( input wire rd_clk , //System clock, frequency 100MHz input wire rd_rst_n , //Reset signal, active at low level input wire init_end , //Initialization end signal input wire rd_en , //Read enable input wire [23:0] rd_addr , //Read SDRAM address input wire [15:0] rd_data , //Data read from SDRAM input wire [9:0] rd_burst_len , //Burst length output wire rd_ack , //Read SDRAM response signal output wire rd_end , //End of a burst read output reg [3:0] rd_sdram_cmd , //Instructions written to sdram in the read data stage, {cs_n,ras_n,cas_n,we_n} output reg [1:0] rd_sdram_bank , //Read data stage Bank address output reg [12:0] rd_sdram_addr , //Address data, auxiliary precharge operation, row and column address, A12-A0,13 bit address output wire [15:0] rd_sdram_data //Data read out by SDRAM ); //********************************************************************// //****************** Parameter and Internal Signal *******************// //********************************************************************// //parameter define parameter TRCD = 10'd2 , //Activation waiting period TCL = 10'd3 , //incubation period TRP = 10'd4 ; //Precharge waiting period parameter RD_IDLE = 4'b0000 , //free RD_ACT = 4'b0001 , //Row activation RD_TRCD = 4'b0011 , //Activation wait RD_READ = 4'b0010 , //Read operation RD_CL = 4'b0100 , //incubation period RD_DATA = 4'b0101 , //Read data RD_PRE = 4'b0111 , //Precharge RD_TRP = 4'b0110 , //Precharge wait RD_END = 4'b1100 ; //End of a burst read parameter NOP = 4'b0111 , //Empty operation instruction ACTIVE = 4'b0011 , //Activate command READ = 4'b0101 , //Data read instruction BURST_STOP = 4'b0110 , //Burst stop command PRECHARGE = 4'b0010 ; //Precharge command //wire define wire trcd_end_flag ; //End of activation waiting cycle wire trp_end_flag ; //End of precharge waiting period wire tcl_end_flag ; //Latency end flag wire tread_end_flag ; //Burst read end wire rdburst_end_flag ; //Read burst termination //reg define reg [3:0] state_cur ; //SDRAM write status reg [3:0] state_next ; //SDRAM write status reg [9:0] cnt_fsm ; //Count the clock cycle, record the waiting time of each initialization state, and the bit width shall meet the requirements of the maximum burst length reg cnt_fsm_reset ; //Clock cycle count reset flag reg [15:0] rd_data_reg ; //********************************************************************// //***************************** Main Code ****************************// //********************************************************************// //rd_data_reg, because there is a phase difference between the data read out from SDRAM and the clock of this module, it needs to be synchronized always@(posedge rd_clk or negedge rd_rst_n)begin if(rd_rst_n == 1'b0) rd_data_reg <= 16'd0; else rd_data_reg <= rd_data; end //rd_end: end of a burst read assign rd_end = (state_cur == RD_END) ? 1'b1 : 1'b0; //rd_ sdram_ Data: data read out by SDRAM assign rd_sdram_data = (rd_ack == 1'b1) ? rd_data_reg : 16'b0; //rd_ack: read SDRAM response signal assign rd_ack = (state_cur == RD_DATA) && (cnt_fsm >= 10'd1) && (cnt_fsm < (rd_burst_len + 2'd1)); //trcd_end,trp_end,tcl_end,tread_end,rdburst_end: wait end flag assign trcd_end_flag = ((state_cur == RD_TRCD) && (cnt_fsm == TRCD)) ? 1'b1 : 1'b0; //End of line gating cycle assign trp_end_flag = ((state_cur == RD_TRP ) && (cnt_fsm == TRP)) ? 1'b1 : 1'b0; //End of precharge validity period assign tcl_end_flag = ((state_cur == RD_CL ) && (cnt_fsm == TCL - 1)) ? 1'b1 : 1'b0; //End of incubation period assign tread_end_flag = ((state_cur == RD_DATA) && (cnt_fsm == rd_burst_len )) ? 1'b1 : 1'b0; //Burst read end assign rdburst_end_flag = ((state_cur == RD_DATA) && (cnt_fsm == rd_burst_len - 4)) ? 1'b1 : 1'b0; //Read burst termination //Reset signal of working state counter always@(*)begin case(state_cur) RD_IDLE: cnt_fsm_reset <= 1'b1; RD_TRCD: cnt_fsm_reset <= (trcd_end_flag == 1'b1) ? 1'b1 : 1'b0; RD_READ: cnt_fsm_reset <= 1'b1; RD_CL: cnt_fsm_reset <= (tcl_end_flag == 1'b1) ? 1'b1 : 1'b0; RD_DATA: cnt_fsm_reset <= (tread_end_flag == 1'b1) ? 1'b1 : 1'b0; RD_TRP: cnt_fsm_reset <= (trp_end_flag == 1'b1) ? 1'b1 : 1'b0; RD_END: cnt_fsm_reset <= 1'b1; default: cnt_fsm_reset <= 1'b0; endcase end //It is used to count each state to realize state jump, and count the reset signal cnt_fsm_reset when reset is valid, and other times are accumulated always@(posedge rd_clk or negedge rd_rst_n)begin if(rd_rst_n == 1'b0) cnt_fsm <= 10'd0; else if(cnt_fsm_reset == 1'b1) cnt_fsm <= 10'd0; else cnt_fsm <= cnt_fsm + 1'b1; end //------------< three stage state machine >---------------------------------------------------------------------------------- //--The first section of state machine: synchronous timing description state transition always@(posedge rd_clk or negedge rd_rst_n)begin if(!rd_rst_n) state_cur <= RD_IDLE; else state_cur <= state_next; end //--The second section of state machine: combinational logic judges the state transition conditions and describes the state transition law and output always@(*)begin state_next = RD_IDLE; case(state_cur) RD_IDLE: if(rd_en && init_end) //If the initialization is completed and the read enable is valid, it will jump to the next state, otherwise it will remain in this state state_next = RD_ACT; else state_next = RD_IDLE; RD_ACT: state_next = RD_TRCD; //Jump to TRCD wait state RD_TRCD: if(trcd_end_flag) //If the TRCD waiting flag is pulled high, it will jump to the next state, otherwise it will remain in this state state_next = RD_READ; else state_next = RD_TRCD; RD_READ: state_next = RD_CL; //Jump latency state RD_CL: if(tcl_end_flag) //If the end of incubation period flag is raised, it will jump to the next state, otherwise it will remain in this state state_next = RD_DATA; else state_next = RD_CL; RD_DATA: //Burst read data status if(tread_end_flag) //If the burst read data end flag is raised, it will jump to the next state, otherwise it will remain in this state state_next = RD_PRE; else state_next = RD_DATA; RD_PRE: state_next = RD_TRP; //Precharge state RD_TRP: if(trp_end_flag) //If the TRP waiting flag is pulled high, it will jump to the next state, otherwise it will remain in this state state_next = RD_END; else state_next = RD_TRP; RD_END: state_next = RD_IDLE; //Jump to initial state default:state_next = RD_IDLE; //The default is in the initial state endcase end //--The third section of state machine: timing logic description output always@(posedge rd_clk or negedge rd_rst_n)begin if(rd_rst_n == 1'b0)begin //Reset the output NOP instruction. If you don't care about the address and BANK address, just pull it up rd_sdram_cmd <= NOP; rd_sdram_bank <= 2'b11; rd_sdram_addr <= 13'h1fff; end else case(state_cur) RD_IDLE,RD_TRCD,RD_TRP,RD_END:begin //Output NOP instruction. If you don't care about the address and BANK address, just raise them all rd_sdram_cmd <= NOP; rd_sdram_bank <= 2'b11; rd_sdram_addr <= 13'h1fff; end RD_ACT:begin rd_sdram_cmd <= ACTIVE; //Activate command rd_sdram_bank <= rd_addr[23:22]; //The BANK address is the input address Rd_ The upper two digits of addr rd_sdram_addr <= rd_addr[21:9]; //The row address is 21-9 bits (13 bit address bus) end RD_READ:begin rd_sdram_cmd <= READ; //Column read operation instruction rd_sdram_bank <= rd_addr[23:22]; //The BANK address is the input address Rd_ The upper two digits of addr rd_sdram_addr <= {4'b0000,rd_addr[8:0]};//The column address has only 9 bits, but shares a 13 bit address bus, so the upper 4 bits are supplemented with 0 end RD_DATA:begin //I don't care about the address and BANK address. Just raise them all rd_sdram_bank <= 2'b11; rd_sdram_addr <= 13'h1fff; if(rdburst_end_flag) rd_sdram_cmd <= BURST_STOP; //Burst transmission termination instruction else rd_sdram_cmd <= NOP; //Burst reading is not finished, send an empty instruction end RD_PRE:begin rd_sdram_cmd <= PRECHARGE; //Precharge command rd_sdram_bank <= rd_addr[23:22]; //The BANK address is the input address Rd_ The upper two digits of addr rd_sdram_addr <= 13'h0400; //A10 pull up and select all banks to precharge all banks. Other bits don't care end default:begin rd_sdram_cmd <= NOP; //Output NOP instruction. If you don't care about the address and BANK address, just raise them all rd_sdram_bank <= 2'b11; rd_sdram_addr <= 13'h1fff; end endcase end endmodule
The code is written in a three-stage state machine. In fact, it can also be written in a linear sequence machine, but the three-stage state machine will standardize points.
Relevant contents of three-stage state machine:
State machines (one-stage, two-stage, three-stage), Moore and Mealy
6,Testbench
In addition to the SDRAM burst read module written before, Testbench also instantiates a PLL module, which outputs 50M, 100M and 100M clock signals with a phase offset of - 30 ° respectively (the PLL module is not given, you can see the SDRAM, can't the PLL?), as well as the SDRAM initialization module designed before (all operations of SDRAM can only be carried out after initialization is completed) .
In addition, a simulation model sdram_model_plus found on the Internet is exemplified. The authors are Li Sheng, Chen naikui and Luo Yao.
`timescale 1ns/1ns module tb_sdram_read(); //********************************************************************// //****************** Internal Signal and Defparam ********************// //********************************************************************// //wire define //clk_gen wire clk_50m ; //PLL output 50M clock wire clk_100m ; //PLL output 100M clock wire clk_100m_shift ; //PLL output 100M clock, phase offset - 30deg wire locked ; //PLL clock lock signal wire rst_n ; //Reset signal, low active //sdram_init wire [3:0] init_cmd ; //Initialization phase instruction wire [1:0] init_bank ; //L-Bank address in initialization stage wire [12:0] init_addr ; //Initialization phase address bus wire init_end ; //Initialization completion signal //sdram_write wire [12:0] wr_sdram_addr ; //Data write phase address bus wire [1:0] wr_sdram_bank ; //L-Bank address in data writing stage wire [3:0] wr_sdram_cmd ; //Data write stage instruction wire [15:0] wr_sdram_data ; //SDRAM data is written in the data write phase wire wr_sdram_en ; //Data write phase write data valid enable signal wire wr_end ; //The data write phase ends with a burst write wire sdram_wr_ack ; //Data write phase write response //sdram_read wire [12:0] rd_sdram_addr ; //Data read phase address bus wire [1:0] rd_sdram_bank ; //L-Bank address in data reading stage wire [3:0] rd_sdram_cmd ; //Data read stage instruction wire [15:0] rd_sdram_data ; //SDRAM data is read in the data reading phase wire rd_end ; //The data reading phase ends with a burst write //sdram_addr wire [12:0] sdram_addr ; //SDRAM address bus wire [1:0] sdram_bank ; //Sdraml bank address wire [3:0] sdram_cmd ; //SDRAM instruction wire [15:0] sdram_dq ; //SDRAM data bus wire [12:0] w_r_addr ; //Data read phase address bus wire [1:0] w_r_bank ; //L-Bank address in data reading stage wire [3:0] w_r_cmd ; //Data read stage instruction //reg define reg sys_clk ; //System clock reg sys_rst_n ; //Reset signal reg wr_en ; //Write enable reg [15:0] wr_data_in ; //Write data reg rd_en ; //Read enable //defparam //Redefine the relevant parameters in the simulation model defparam sdram_model_plus_inst.addr_bits = 13; //Address bit width defparam sdram_model_plus_inst.data_bits = 16; //Data bit width defparam sdram_model_plus_inst.col_bits = 9; //Column address bit width defparam sdram_model_plus_inst.mem_sizes = 2*1024*1024; //L-Bank capacity //********************************************************************// //**************************** Clk And Rst ***************************// //********************************************************************// //Clock, reset signal initial begin sys_clk = 1'b1 ; sys_rst_n <= 1'b0 ; #200 sys_rst_n <= 1'b1 ; end always #10 sys_clk = ~sys_clk; //rst_n: reset signal assign rst_n = sys_rst_n & locked; //wr_en: write data enable always@(posedge clk_100m or negedge rst_n) if(rst_n == 1'b0) wr_en <= 1'b1; else if(wr_end == 1'b1) wr_en <= 1'b0; else wr_en <= wr_en; //wr_data_in: write data always@(posedge clk_100m or negedge rst_n) if(rst_n == 1'b0) wr_data_in <= 16'd0; else if(wr_data_in == 16'd10) wr_data_in <= 16'd0; else if(sdram_wr_ack == 1'b1) wr_data_in <= wr_data_in + 1'b1; else wr_data_in <= wr_data_in; //rd_en: read data enable always@(posedge clk_100m or negedge rst_n) if(rst_n == 1'b0) rd_en <= 1'b0; else if(rd_end == 1'b1) rd_en <= 1'b0; else if(wr_en == 1'b0) rd_en <= 1'b1; else rd_en <= rd_en; //sdram_cmd,sdram_bank,sdram_addr assign sdram_cmd = (init_end == 1'b1) ? w_r_cmd : init_cmd; assign sdram_bank = (init_end == 1'b1) ? w_r_bank : init_bank; assign sdram_addr = (init_end == 1'b1) ? w_r_addr : init_addr; //w_r_cmd,w_r_bank,w_r_addr assign w_r_cmd = (wr_en == 1'b1) ? wr_sdram_cmd : rd_sdram_cmd; assign w_r_bank = (wr_en == 1'b1) ? wr_sdram_bank : rd_sdram_bank; assign w_r_addr = (wr_en == 1'b1) ? wr_sdram_addr : rd_sdram_addr; //wr_sdram_data assign sdram_dq = (wr_sdram_en == 1'b1) ? wr_sdram_data : 16'hz; //********************************************************************// //*************************** Instantiation **************************// //********************************************************************// //------------- clk_gen_inst ------------- clk_gen clk_gen_inst ( .inclk0 (sys_clk ), .areset (~sys_rst_n ), .c0 (clk_50m ), .c1 (clk_100m ), .c2 (clk_100m_shift ), .locked (locked ) ); //------------- sdram_init_inst ------------- sdram_init sdram_init_inst( .init_clk (clk_100m ), .init_rst_n (rst_n ), .init_cmd (init_cmd ), .init_bank (init_bank ), .init_addr (init_addr ), .init_end (init_end ) ); //------------- sdram_write_inst ------------- sdram_write sdram_write_inst( .wr_clk (clk_100m ), .wr_rst_n (rst_n ), .init_end (init_end ), .wr_en (wr_en ), .wr_addr (24'h000_000 ), .wr_data (wr_data_in ), .wr_burst_len (10'd10 ), .wr_ack (sdram_wr_ack ), .wr_end (wr_end ), .wr_sdram_cmd (wr_sdram_cmd ), .wr_sdram_bank (wr_sdram_bank ), .wr_sdram_addr (wr_sdram_addr ), .wr_sdram_en (wr_sdram_en ), .wr_sdram_data (wr_sdram_data ) ); //------------- sdram_read_inst ------------- sdram_read sdram_read_inst( .rd_clk (clk_100m ), .rd_rst_n (rst_n ), .init_end (init_end ), .rd_en (rd_en ), .rd_addr (24'h000_000 ), .rd_data (sdram_dq ), .rd_burst_len (10'd10 ), .rd_ack ( ), .rd_end (rd_end ), .rd_sdram_cmd (rd_sdram_cmd ), .rd_sdram_bank (rd_sdram_bank ), .rd_sdram_addr (rd_sdram_addr ), .rd_sdram_data (rd_sdram_data ) ); //-------------sdram_model_plus_inst------------- sdram_model_plus sdram_model_plus_inst( .Dq (sdram_dq ), .Addr (sdram_addr ), .Ba (sdram_bank ), .Clk (clk_100m_shift ), .Cke (1'b1 ), .Cs_n (sdram_cmd[3] ), .Ras_n (sdram_cmd[2] ), .Cas_n (sdram_cmd[1] ), .We_n (sdram_cmd[0] ), .Dqm (2'b0 ), .Debug (1'b1 ) ); //------------------------------------------------ //--State machine name viewer //------------------------------------------------ reg [79:0] name_state_cur; //Each character is 8 bits wide. Here, take up to 10 characters and 80 bits wide always @(*) begin case(sdram_read_inst.state_cur) 4'b0000: name_state_cur = "RD_IDLE "; 4'b0001: name_state_cur = "RD_ACT "; 4'b0011: name_state_cur = "RD_TRCD "; 4'b0010: name_state_cur = "RD_READ "; 4'b0100: name_state_cur = "RD_CL "; 4'b0101: name_state_cur = "RD_DATA "; 4'b0111: name_state_cur = "RD_PRE "; 4'b0110: name_state_cur = "RD_TRP "; 4'b1100: name_state_cur = "RD_END "; default: name_state_cur = "RD_IDLE "; endcase end //------------------------------------------------ endmodule
7. Simulation results
The results are as follows:
Above:
- After the read request rd_en is pulled high, init_end is high at this time, so it enters the sending line activation state in the next cycle, and the sending line activation command + line address + bank address
- After waiting for TRCD clock cycles, enter the column read-write state and send the read instruction + column head address + bank address
- After waiting for CL clock cycles, the first data is output on the data bus
- Thereafter, the data bus outputs data until the burst is terminated
- After sending the precharge command, wait to meet the TRP timing requirements of 2 clocks and enter the RD_END state
- In the rd_end state, pull up the output signal rd_end for one cycle to notify the arbitration module that the reading operation has ended, so as to facilitate the arbitration module to schedule
The information printed in the modlesim command window is as follows:
You can see:
- After the first initialization, the burst write operation is performed. The burst write length is 10, the write data are 1-10 respectively, and the write address is 0-10 of the first line of bank00
- Since then, the above address has been read, and the read data is consistent with the written data
8. Summary
- This full page burst read operation with burst termination is easier to control the read length and is much faster than a single read
- For those who want engineering documents, please leave an email in the comments and I'll send it to you as soon as possible