FPGA实战操作(1) -- SDRAM(Verilog实现)
對SDRAM基本概念的介紹以及芯片手冊說明,請參考上一篇文章SDRAM操作說明。
1. 說明
如圖所示為狀態(tài)機的簡化圖示,過程大概可以描述為:SDRAM(IS42S16320D)上電初始化完成后,進入“空閑”狀態(tài),此時一直監(jiān)控外部控制模塊給予的控制信號。初始化完成后,外部定時器開始定時,定時周期為SDRAM刷新周期(7.7us),一旦計數(shù)到刷新周期后,向狀態(tài)機發(fā)送auto_ref_req(自動刷新請求),此時狀態(tài)機進入“刷新”狀態(tài),這樣就確保在無任何操作時,SDRAM能正常完成刷新。刷新完成后回到“空閑”狀態(tài)。
當(dāng)處于空閑狀態(tài)時,接收到寫命令(wr_en),進入“寫”狀態(tài)(有效接收讀寫命令的時刻有特殊要求,后面再詳細說明),在full_page下連續(xù)寫600個數(shù)據(jù)(100MHz,恰好耗時6us多一點,這樣方便不用考慮定時刷新),寫完之后,發(fā)送wr_done命令,進入“刷新”狀態(tài),相對于每次連續(xù)寫完成后,提前刷新一次。此時,定時刷新的計數(shù)器清零,重新開始計數(shù)。
讀多過程跟寫過程類似,讀完600個數(shù)據(jù)之后,手動完成刷新。
現(xiàn)在就來說一說,“空閑”狀態(tài)接收讀寫命令的特殊要求。理論上充電周期為7.8125us,為保證600次讀寫在充電周期內(nèi)完成,并且前后預(yù)留一些其他命令的時間,所以推薦在0~1us這個時間內(nèi)接受讀寫命令,這樣讀寫的時候?qū)W⒆x寫就可以了。當(dāng)然這是我的設(shè)計方式,如有更好的設(shè)計方式,那更好,歡迎分享。
2. 代碼實現(xiàn)
狀態(tài)機的代碼如下所示,清晰的描述了各狀態(tài)之間的跳變及其跳變條件。其中信號ctrl_valid即為上圖中命令有效期的時間區(qū)間。在各狀態(tài)描述的時序邏輯模塊中,只是產(chǎn)生了讀、寫或刷新執(zhí)行模塊的使能信號,即在“寫”狀態(tài)的時候,使能寫模塊,完成相信的寫操作。
always @ (posedge clk or negedge rst_n)beginif(rst_n == 1'b0)begincurrent_status <= IDLE;endelse if(init_ing == 1'b0)begincurrent_status <= next_status;endelsebegincurrent_status <= IDLE;endendalways @ (rst_n or current_status or sdram_wrreq or sdram_rdreq or ref_req_auto or wr_done or rd_done or ref_done or ctrl_valid)beginnext_status = 5'dx;case(current_status)IDLE:beginif(ref_req_auto == 1'b1) //收到自動刷新請求beginnext_status = AUTO_REF;endelse if(ctrl_valid == 1'b1 && sdram_wrreq == 1'b1)//在讀寫控制有效區(qū)內(nèi)收到寫請求beginnext_status = WRITE;endelse if(ctrl_valid == 1'b1 && sdram_rdreq == 1'b1) //在讀寫控制有效區(qū)內(nèi)收到讀請求beginnext_status = READ;endelsebeginnext_status = IDLE;endendWRITE:beginif(wr_done == 1'b1)beginnext_status = AUTO_REF;endelsebeginnext_status = WRITE;end endREAD:beginif(rd_done == 1'b1)beginnext_status = AUTO_REF;endelsebeginnext_status = READ;end endAUTO_REF:beginif(ref_done == 1'b1)beginnext_status = IDLE;endelsebeginnext_status = AUTO_REF;endenddefault:beginnext_status = IDLE;end endcaseend//各個狀態(tài)下的使能信號,以控制相應(yīng)的模塊執(zhí)行相應(yīng)的操作 always @ (posedge clk or negedge rst_n)beginif(rst_n == 1'b0)beginwr_start <= 1'b0;rd_start <= 1'b0;ref_start <= 1'b0;endelsebegincase(next_status)IDLE:beginwr_start <= 1'b0;rd_start <= 1'b0;ref_start <= 1'b0;endWRITE:beginwr_start <= 1'b1;rd_start <= 1'b0;ref_start <= 1'b0;endREAD:beginwr_start <= 1'b0;rd_start <= 1'b1;ref_start <= 1'b0;endAUTO_REF:beginwr_start <= 1'b0;rd_start <= 1'b0;ref_start <= 1'b1;enddefault:beginwr_start <= 1'b0;rd_start <= 1'b0;ref_start <= 1'b0;endendcaseendend以下給出寫操作模塊的部分代碼,讀操作和刷新同理。中間有些信號是我工程需要,參考一下思路即可。
always @(posedge clk or negedge rst_n)beginif(rst_n == 1'b0)begincke_wr <= 1'b0;cmd_wr <= NOP;dqm_wr <= DQM_DIS;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b0;wr_first_flag_r <= 1'b0;status_wr <= 4'd0;endelse if(wr_start == 1'b1)begincase(status_wr)4'd0:begin cke_wr <= 1'b1;cmd_wr <= NOP;dqm_wr <= DQM_EN;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b0;wr_first_flag_r <= 1'b0;status_wr <= status_wr + 4'd1; end4'd1:begincke_wr <= 1'b1;cmd_wr <= ACT;dqm_wr <= DQM_EN;bank_addr_wr <= BANK0;addr_wr <= row_addr; //行地址wr_done <= 1'b0;wr_first_flag_r <= 1'b0;status_wr <= status_wr + 4'd1;end4'd2: //4'd2和4'd3是為了延時T_RCD,即兩個時鐘begincke_wr <= 1'b1;cmd_wr <= NOP;dqm_wr <= DQM_EN;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b0;wr_first_flag_r <= 1'b0; status_wr <= status_wr + 4'd1;end4'd3: begincke_wr <= 1'b1;cmd_wr <= NOP;dqm_wr <= DQM_EN;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b0;wr_first_flag_r <= 1'b0; status_wr <= status_wr + 4'd1;end4'd4:begincke_wr <= 1'b1;cmd_wr <= NOP;dqm_wr <= DQM_EN;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b0;wr_first_flag_r <= 1'b1; //用于寫入第一個數(shù)據(jù)的時序標(biāo)記status_wr <= status_wr + 4'd1;end4'd5:begincke_wr <= 1'b1;cmd_wr <= WR;dqm_wr <= DQM_EN;bank_addr_wr <= BANK0;addr_wr <= column_addr; //{A12A11,A10,column_address}wr_done <= 1'b0;wr_first_flag_r <= 1'b0;status_wr <= status_wr + 4'd1;end4'd6:beginif(sdram_wr_done == 1'b1) //用于增加NOP持續(xù)周期begincke_wr <= 1'b1;cmd_wr <= NOP;dqm_wr <= DQM_DIS;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b1;wr_first_flag_r <= 1'b0;status_wr <= status_wr + 4'd1;endelsebegincke_wr <= 1'b1;cmd_wr <= NOP;dqm_wr <= DQM_EN;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b0;wr_first_flag_r <= 1'b0;status_wr <= status_wr;endend4'd7:begincke_wr <= 1'b1;cmd_wr <= NOP;dqm_wr <= DQM_DIS;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b0;wr_first_flag_r <= 1'b0;status_wr <= 4'd0;enddefault:begincke_wr <= 1'b1;cmd_wr <= NOP;dqm_wr <= DQM_EN;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b0;wr_first_flag_r <= 1'b0;status_wr <= 4'd0;endendcaseendelsebegincke_wr <= 1'b1;cmd_wr <= NOP;dqm_wr <= DQM_EN;bank_addr_wr <= BANK0;addr_wr <= DONT_CARE_ADDR;wr_done <= 1'b0;wr_first_flag_r <= 1'b0;status_wr <= 4'd0;endend參考文獻
SDRAM驅(qū)動篇之簡易SDRAM控制器的verilog代碼實現(xiàn)
轉(zhuǎn)載于:https://www.cnblogs.com/rouwawa/p/7339102.html
總結(jié)
以上是生活随笔為你收集整理的FPGA实战操作(1) -- SDRAM(Verilog实现)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java基础之访问控制符
- 下一篇: OpenStack概念架构简述