SDRAM回環設計
文章目錄
- SDRAM回環設計
- 一、SDRAM簡介
- 1、引腳
- 2、內部結構框圖
- 3、操作指令
- 二、系統設計
- 三、實現流程
- 1、SDRAM接口
- 2、FIFO設置
- 3、內部SDRAM的控制模塊
- 4、其他
- 四、實現效果
- 五、總結
- 六、代碼
- 1、top
- 2、sdram_top
- 3、sdram_ctrl
一、SDRAM簡介
SDRAM英文全稱“Synchronous Dynamic Random Access Memory”,譯為“同步動態隨機存取內存”或“同步動態隨機存儲器”,是動態隨機存儲器(Dynamic Random Access Memory,簡稱DRAM)家族的一份子。同步、動態、隨機是其性能特點的外在說明,也是其區別其他存儲器的特色標簽。這三個概念性的標簽,我們要好好理解掌握。
同步(Synchronous):與通常的異步DRAM不同, SDRAM存在一個同步接口,其工作時鐘的時鐘頻率與對應控制器(CPU/FPGA)的時鐘頻率相同,并且SDRAM內部的命令發送與數據傳輸均以此時鐘為基準,實現指令或數據的同步操作;
動態(Dynamic): SDRAM需要不斷的刷新來保證存儲陣列內數據不丟失;
隨機(Random):數據在SDRAM中并不是按照線性依次存儲,而是可以自由指定地址進行數據的讀寫。
空間存儲量大、讀寫速度快以及價格相對便宜等優點使其在存儲界屹立不倒、經久不衰,廣泛應用在計算機中。隨著時代的不斷發展、技術的不斷更新,SDRAM使用至今已過數十載,產品更新歷經五代,分別是:第一代SDR SDRAM,第二代DDR SDRAM,第三代DDR2 SDRAM,第四代DDR3 SDRAM,第五代,DDR4 SDRAM。
第一代SDR SDRAM采用單端時鐘信號,SDRAM只在時鐘的上升沿進行數據采樣;而后面的四代SDRAM由于工作頻率比較快,所以采用可降低干擾的差分時鐘信號作為同步時鐘,雙沿采樣,速度更快,且功耗更低。同時技術的不斷發展、制造工藝的不斷提高,使得五代SDRAM的更新過程中,集成度越來越高、內核電壓越來越低(SDR:3.3V 、DDR:2.5V、DDR2:1.8V、DDR3:1.5V、DDR4:1.2V),這也是SDRAM速度提高、功耗降低的重要原因。
本次運用的SDRAM為海力士公司的H57V2562GTR-75C,內存大小為256Mbit,有4個bank,每個bank有4M(8192行*512列)存儲單元,每個單元可存16bit數據
1、引腳
信號 | 類型 | 描述 |
---|---|---|
clk | in | 時鐘,所有其他輸入在CLK的上升沿被寄存到SDRAM |
cke | in | 時鐘使能,控制內部時鐘信號,當禁用時,SDRAM將處于電源關閉、暫停或自刷新狀態之一 |
cs_n | in | 片選,啟用或禁用除CLK、CKE和DQM之外的所有輸入 |
BA0,BA1 | in | bank地址線 |
A0-A12 | in | 行列地址線,行地址:RA0 ~ RA12,列地址:CA0 ~ CA8自動預充電標志:A10 |
RAS_n,CAS_n,WE_N | in | 行地址選通,列地址選通,寫使能 |
LDQM,UDQM | I/O | 數據掩碼,16位高低字節,可控制讀寫數據 |
DQ0-DQ15 | I/O | 數據輸入輸出 |
2、內部結構框圖
由圖可知,SDRAM內部包含一個狀態機左上角state machine),外部通過控制CS_N、RAS_N、CAS_N、WE_N、A0-A12數據地址線以及BA0、BA1 BANK地址線來發送命令,命令經過解碼器進行譯碼后,將控制參數保存到模式寄存器中,邏輯控制單元進而控制邏輯運行。
外部通過地址總線輸入地址信息,地址信息在邏輯控制單元進行邏輯控制時起到輔助作用,除此之外,復用的地址總線與Bank控制邏輯、行地址復用器、列地址計數鎖存器、列地址解碼器等內部器件共同作用,精確選定存儲陣列中與行列地址相對應的存儲單元,進而進行數據存取操作。
DQM數據掩碼線控制這數據是否有效,其又L低位與U高位兩條線,每條控制著8bit數據。
DQ0-DQ15為數據線,其為雙向,讀寫共用一條線。
3、操作指令
1、預充電(precharge)
預充電命令:關閉特定bank中激活的行,或關閉所有bank中激活的行。
A10決定預充電模式:
當預充電命令中的地址A10為高時,預充電所有bank;當預充電命令中A10為低時,預充電特定bank。
當write/read命令中的A10為高時,自動預充電(auto-precharge)被使能;反之。
預充電命令發送之后,這些bank等待tRP才能接收命令
2、自動預充電(auto-precharge)
自動預充電是非顯示命令,即使能自動預充電是需要發送write/read命令時將地址中A10拉高,在讀寫
突發結束后,立即預充電那個bank/row。
3、NOP
NOP 指令用以表明對 sdram 芯片(CS# == 0)進行空操作。NOP 指令的目的是在 sdram 在空閑
或者等待狀態下,避免去執行一些潛在的不需要的指令。已經在執行過程中的指令不受影響。
4、自動刷新(auto-refresh)與自刷新(self-refresh)
為使數據不丟失,電容的兩次刷新時間不能超過64ms,刷新都是針對行的。共性:都不需要外部提供地址信息,SDRAM內部有一個行地址生成器(刷新計數器)。刷新都是針對
一行的,不需要對列地址尋址,但也不需要對行進行尋址,因為內部有刷新計數器
自動刷新:SDRAM正常工作模式中為了數據不丟失進行的操作,需要外部時鐘參與,刷新的行地址也
是由內部刷新計算器控制
自刷新:休眠模式低功耗狀態下存儲數據,不需要外部時鐘參與,刷新的行地址內部刷新計算器控
制。
發送自動刷新命令需要的時間為tRRC(自動刷新周期),由于是對行操作,等效于行選通時間(RAS)
SDRAM中每次刷新操作所需要的時間為自動刷新周期(tRC),在自動刷新指令發出后需要等待tRC才能發
送其他指令。
5、行激活
行激活命令也叫做bank激活命令,作用是在指定bank中激活一行;
行激活后會一直處于激活狀態(即列尋址處于激活狀態),直到預充電命令被發送到這個bank;
行激活命令之后,需要延時tRCD(即發出行地址到發出列地址的時間間隔),才能發送READ/WRITE命令(讀寫操作必須先激活對應bank)
6、讀操作
讀數據命令用來開啟數據的突發讀,bank,row都可選,注意A10的值決定是否執行自動預充電操作,若執行自動預充電,突發讀結束后就進行預充電,此行關閉,若不執行自動預充電,該行保持激活,仍能被訪問。
CAS latency :讀延遲(讀潛伏周期,CL)Burst length:突發長度(BL)
讀命令發出后,輸出buffer(理解為SDRAM的dq_out)會在(CL-1)個時鐘后期后變為低阻,然后會在突發讀結束后重新變為高阻態,
7、寫操作
DM(數據掩碼)高有效,當為低時,數據能正確被寫入DM;當為高時,數據將被忽略。
寫突發時,第一個數據與寫命令同步;
突發寫–>預充電中間需要間隔tDPL。
二、系統設計
所實現功能 :pc發送數據,存入fifo的數據達到所設閾值,進行突發寫操作(此處用的ip核不支持多bit突發讀寫操作,故在控制模塊中實現突發讀寫操作),當按鍵(經過消抖)按下,進行突發讀操作,將讀取的數據發送回pc顯示出來。
三、實現流程
1、SDRAM接口
SDRAM接口部分由ip核實現
1) 選擇Platform Designer
2) 在左上角處搜索sdram,在下方雙擊選中ip添加
3) 配置各個參數
本次運用的SDRAM的數據寬度為16bit,有4個bank,每個bank有13行9列
參數 | 含義 | 典型值意義 |
---|---|---|
CAS latency cycles | 讀命令發出后,到第一個有效數據出現在總線上的時鐘周期數 | 設為 3 → 3 個時鐘 |
Initialization refresh cycles | 上電后、正式初始化前,控制器自動發出的 Auto-Refresh 命令次數 | 8 次 |
Issue one refresh command every | 兩次刷新命令之間的間隔時間 | 7.8 μs(SDRAM 要求 64 ms 內刷完所有行,因此 7.8 μs/行≈8192 行) |
Delay after powerup, before initialization | 芯片上電穩定后再開始初始化的等待時間 | 200 μs(JEDEC 規范建議 ≥100 μs) |
Duration of refresh command (t_rfc) | 單個 Auto-Refresh 命令占用總線的最短時間 | 70 ns |
Duration of precharge command (t_rp) | 關閉當前行(Precharge)所需時間 | 20 ns |
ACTIVE to READ or WRITE delay (t_rcd) | 行激活到列讀/寫命令之間的最小間隔 | 20 ns |
Access time (t_ac) | 時鐘沿到數據有效輸出的延遲(器件參數,非用戶設) | 5 ns |
Write recovery time (t_wr) | 寫命令結束后到允許 Precharge 的最小間隔 | 20 ns |
配置完如下
4) 點擊右下角finish左邊按鈕出現以下界面
改為生成verilog代碼,路徑自行選擇,更改完成點擊生成,等待完成,等待時間因電腦配置有差異
生成完成,依次點擊close、finish
5) finish后出現以下界面提示添加文件
添加完成在ip查看界面才會有顯示
生成的例化模板:
sdram_ip u0 (.clk_clk (), // clk.clk.reset_reset_n (), // reset.reset_n.sdram_addr (), // sdram.addr.sdram_ba (), // .ba.sdram_cas_n (), // .cas_n.sdram_cke (), // .cke.sdram_cs_n (), // .cs_n.sdram_dq (), // .dq.sdram_dqm (), // .dqm.sdram_ras_n (), // .ras_n.sdram_we_n (), // .we_n.avs_address (), // avs.address.avs_byteenable_n (), // .byteenable_n.avs_chipselect (), // .chipselect.avs_writedata (), // .writedata.avs_read_n (), // .read_n.avs_write_n (), // .write_n.avs_readdata (), // .readdata.avs_readdatavalid (), // .readdatavalid.avs_waitrequest () // .waitrequest);
端口名稱 | 方向 | 位寬 | 含義說明 |
---|---|---|---|
clk_clk | input | 1 | 全局系統時鐘,用于 SDRAM 控制器內部邏輯以及 SDRAM 芯片。 |
reset_reset_n | input | 1 | 全局異步復位,低有效。 |
sdram_addr[12:0] | output | 13 | SDRAM 地址總線,行列地址復用。 |
sdram_ba[1:0] | output | 2 | Bank 地址,選擇 SDRAM 的 4 個 bank。 |
sdram_cas_n | output | 1 | 列地址選通,低有效。 |
sdram_cke | output | 1 | 時鐘使能,高時 SDRAM 響應時鐘。 |
sdram_cs_n | output | 1 | 片選,低有效。 |
sdram_dq[15:0] | inout | 16 | 雙向數據總線。 |
sdram_dqm[1:0] | output | 2 | 數據掩碼/字節使能,對應 16bit 數據的高低字節。 |
sdram_ras_n | output | 1 | 行地址選通,低有效。 |
sdram_we_n | output | 1 | 寫使能,低有效。 |
avs_address[21:0] | input | 22 | Avalon-MM 總線地址(字節地址)。 |
avs_byteenable_n[1:0] | input | 2 | 字節使能,低有效,對應 16bit 數據高低字節。 |
avs_chipselect | input | 1 | Avalon-MM 片選,高有效。 |
avs_writedata[15:0] | input | 16 | 寫數據。 |
avs_read_n | input | 1 | 讀請求,低有效。 |
avs_write_n | input | 1 | 寫請求,低有效。 |
avs_readdata[15:0] | output | 16 | 讀回數據。 |
avs_readdatavalid | output | 1 | 讀數據有效指示,高有效。 |
avs_waitrequest | output | 1 | 控制器忙,拉低時表示需要等待。 |
sdram前綴為輸出給sdram芯片的接口,avs前綴為內部的控制的Avalon協議接口。
2、FIFO設置
//---------<寫FIFO例化>------------------------------------------------- wr_fifo wr_fifo_inst (.aclr ( ~rst_n ),.data ( {2{rx_data}} ),.rdclk ( clk ),.rdreq ( wr_rden ),.wrclk ( clk_in ),.wrreq ( wr_wren ),.q ( wr_q ),.rdempty ( wr_rdempty ),.rdusedw ( wr_rdusedw ),.wrfull ( wr_wrfull ));assign wr_wren = ~wr_wrfull && rx_vld;
assign wr_rden = ~wr_rdempty && (state_c == WRITE) && ~avm_waitrequest;
由于所設置SDRAM寬度為16,但uart_rx傳入為8bit,所以拼兩個傳入的數據存入SDRAM。
寫使能(wr_wren):fifo未滿且rx傳入的數據有效,就將數據寫入fifo
讀使能(wr_rden):fifo不空且處于寫狀態,且sdram處于不忙狀態(waitrequest為低)將數據傳給sdram
//---------<讀FIFO例化>------------------------------------------------- rd_fifo rd_fifo_inst (.aclr ( ~rst_n ),.data ( avm_readdata ),.rdclk ( clk_out ),.rdreq ( rd_rden ),.wrclk ( clk ),.wrreq ( rd_wren ),.q ( rd_q ),.rdempty ( rd_rdempty ),.wrfull ( rd_wrfull )
);assign rd_wren = ~rd_wrfull && avm_readdatavalid;
assign rd_rden = ~rd_rdempty && tx_done;
寫使能(rd_wren):fifo未滿且sdram處于不忙狀態(waitrequest為低)將數據傳給讀fifo
讀使能(rd_rden):fifo不空且tx發送模塊不忙,將數據傳給tx發送到pc
3、內部SDRAM的控制模塊
由于uart時鐘與sdram的時鐘不同,這里用了兩個異步fifo,一個用于寫,一個用于讀;wrfifo寫側為uart_rx輸入的系統時鐘,即為clk_in,讀側為輸出到sdram的時鐘,為clk_100m;rdfifo寫側為sdram傳出的數據,時鐘為clk_100m,讀側為通過uart_tx傳回pc的數據接口,時鐘為系統時鐘。
在此設置一個簡單的狀態機實現讀寫操作,此處的burst_lenth為 10。
//---------<突發計數器>------------------------------------------------- always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_burst <= 'd0;end else if(add_cnt_burst)begin if(end_cnt_burst)begin cnt_burst <= 'd0;endelse begin cnt_burst <= cnt_burst + 1'b1;end end
end assign add_cnt_burst = ((state_c == READ) || (state_c == WRITE)) && ~avm_waitrequest;
assign end_cnt_burst = add_cnt_burst && cnt_burst == (BURST_LENTH-1);
當按鍵按下進入讀數據狀態,開始從sdram讀取數據,當讀fifo中讀了10個數據時跳出。
當寫fifo的數據大于所設突發操作的閾值,就進行寫入操作,跳轉到write狀態,將10個數據寫入sdram,寫完10個后跳到空閑狀態。
讀寫地址通過計數器實現
//---------<讀寫地址>-------------------------------------------------
//寫地址計數
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginwr_addr <= 'd0;end else if(add_wr_addr)begin if(end_wr_addr)begin wr_addr <= 'd0;endelse begin wr_addr <= wr_addr + 1'b1;end end
end assign add_wr_addr = (state_c == WRITE) && ~avm_waitrequest;
assign end_wr_addr = add_wr_addr && wr_addr == 24'hff_ff_ff-1;//讀地址計數
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_addr <= 'd0;end else if(add_rd_addr)begin if(end_rd_addr)begin rd_addr <= 'd0;endelse begin rd_addr <= rd_addr + 1'b1;end end
end assign add_rd_addr = (state_c == READ) && ~avm_waitrequest;
assign end_rd_addr = add_rd_addr && rd_addr == 24'hff_ff_ff-1;
由于地址線共用,所以要判斷現在是讀狀態還是寫狀態
//---------<狀態判斷>-------------------------------------------------
assign avm_address = (state_c == WRITE) ? {wr_addr[23],wr_addr[21:9],wr_addr[22],wr_addr[8:0]} : {rd_addr[23],rd_addr[21:9],rd_addr[22],rd_addr[8:0]} ;
其他輸出
assign tx_data = rd_q [7:0];
assign tx_vld = rd_rden;
assign avm_writedata = wr_q;
assign avm_read_n = ~(state_c == READ);
assign avm_write_n = ~(state_c == WRITE);
4、其他
其他模塊復用之前的。
四、實現效果
直接按下按鍵讀出錯亂的數據,按下復位,發送數據,將數據寫入sdram
按下兩次按鍵讀出數據
讀出的20個數據與發送的前20個相同,驗證成功。
五、總結
相較于之前的幾個存儲器來說,sdram是并行的,且有其他的各種信號,端口較多比較復雜,且內部要用avalon協議傳輸數據,連線和數據之間的傳輸較為復雜,需要認真理解,本實驗主要目的為理解sdram的操作和工作原理。
六、代碼
1、top
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:03:14
Description: 項目頂層
********************************************************************/module top (input clk ,input rst_n ,input [1:0] sw ,
//---------<sdram>------------------------------------------------- output [12:0] sdram_addr ,output [1:0] sdram_ba ,output sdram_cas_n ,output sdram_cke ,output sdram_cs_n ,inout [15:0] sdram_dq ,output [1:0] sdram_dqm ,output sdram_ras_n ,output sdram_we_n ,output sdram_clk ,
//---------<uart>------------------------------------------------- input rx ,output tx ,
//---------<key>------------------------------------------------- input [0:0] key_in
);wire clk_100ms;
wire clk_100ms_s;
wire locked;//---------<uart_rx>-------------------------------------------------
wire [7:0] rx_data;
wire rx_vld;
//---------<uart_tx>-------------------------------------------------
wire [7:0] tx_data;
wire tx_vld;
wire tx_done;
//---------<key>-------------------------------------------------
wire key_down; assign sdram_clk = clk_100ms_s;
sdram_top inst_sdram_top(.clk (clk_100ms ),.clk_in (clk ),.clk_out (clk ),.rst_n (rst_n ),.rx_data (rx_data ),.rx_vld (rx_vld ),.tx_done (tx_done ),.tx_data (tx_data ),.tx_vld (tx_vld ),.key_down (key_down ),.sdram_addr (sdram_addr ),.sdram_ba (sdram_ba ),.sdram_cas_n(sdram_cas_n),.sdram_cke (sdram_cke ),.sdram_cs_n (sdram_cs_n ),.sdram_dq (sdram_dq ),.sdram_dqm (sdram_dqm ),.sdram_ras_n(sdram_ras_n),.sdram_we_n (sdram_we_n )
);sdram_pll sdram_pll_inst (.areset ( ~rst_n ),.inclk0 ( clk ),.c0 ( clk_100ms ),.c1 ( clk_100ms_s ),.locked ( locked ));uart_rx inst_uart_rx(.clk (clk ),.rst_n (rst_n ),.rx (rx ),.sw (sw ),.rx_data (rx_data ),.rx_done (rx_vld )
);uart_tx inst_uart_tx(.clk (clk ),.rst_n (rst_n ),.tx_data (tx_data ),.tx_start(tx_vld ),.sw (sw ),.tx (tx ),.tx_done (tx_done )
);fsm_key inst_fsm_key(.clk (clk ),.rst_n (rst_n ),.key_in (key_in ),.key_down (key_down)
);
endmodule
2、sdram_top
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:02:40
Description: SDRAM子頂層
********************************************************************/module sdram_top( input clk , //100mHzinput clk_in , //50mHzinput clk_out , //50mHzinput rst_n ,
//---------<uart_rx>------------------------------------------------- input [7:0] rx_data ,input rx_vld ,
//---------<uart_tx>------------------------------------------------- input tx_done ,output [7:0] tx_data ,output tx_vld ,
//---------<key>------------------------------------------------- input key_down ,
//---------<sdram_ip>------------------------------------------------- output [12:0] sdram_addr ,output [1:0] sdram_ba ,output sdram_cas_n ,output sdram_cke ,output sdram_cs_n ,inout [15:0] sdram_dq ,output [1:0] sdram_dqm ,output sdram_ras_n ,output sdram_we_n
);wire [23:0] avm_address ;
wire [15:0] avm_writedata ;
wire avm_read_n ;
wire avm_write_n ;
wire [15:0] avm_readdata ;
wire avm_readdatavalid ;
wire avm_waitrequest ;sdram_ctrl inst_sdram_ctrl(.clk (clk ),.clk_in (clk_in ),.clk_out (clk_out ),.rst_n (rst_n ),.rx_data (rx_data ),.rx_vld (rx_vld ),.tx_done (tx_done ),.tx_data (tx_data ),.tx_vld (tx_vld ),.key_down (key_down ),.avm_address (avm_address ),.avm_writedata (avm_writedata ),.avm_read_n (avm_read_n ),.avm_write_n (avm_write_n ),.avm_readdata (avm_readdata ),.avm_readdatavalid(avm_readdatavalid),.avm_waitrequest (avm_waitrequest )
);sdram_ip inst_sdram_ip(.avs_address (avm_address ), .avs_byteenable_n (2'b00 ), .avs_chipselect (1'b1 ), .avs_writedata (avm_writedata ), .avs_read_n (avm_read_n ), .avs_write_n (avm_write_n ), .avs_readdata (avm_readdata ), .avs_readdatavalid(avm_readdatavalid),.avs_waitrequest (avm_waitrequest ), .clk_clk (clk ), .reset_reset_n (rst_n ), .sdram_addr (sdram_addr ), .sdram_ba (sdram_ba ), .sdram_cas_n (sdram_cas_n ), .sdram_cke (sdram_cke ), .sdram_cs_n (sdram_cs_n ), .sdram_dq (sdram_dq ), .sdram_dqm (sdram_dqm ), .sdram_ras_n (sdram_ras_n ), .sdram_we_n (sdram_we_n )
); endmodule
3、sdram_ctrl
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:05:40
Description: SDRAM控制模塊
********************************************************************/module sdram_ctrl #(parameter BURST_LENTH = 10)(input clk , //100mHzinput clk_in , //50mHzinput clk_out , //50mHzinput rst_n ,
//---------<uart_rx>---------------------------------input [7:0] rx_data ,input rx_vld ,
//---------<uart_tx>---------------------------------input tx_done ,output [7:0] tx_data ,output tx_vld ,
//---------<key>-------------------------------------input key_down ,
//---------<sdram_ip>------------------------------------------------- output [23:0] avm_address , output [15:0] avm_writedata , output avm_read_n ,output avm_write_n ,input [15:0] avm_readdata ,input avm_readdatavalid ,input avm_waitrequest
);//rd_fifo
wire rd_rden ;
wire rd_wren ;
wire [15:0] rd_q ;
wire rd_rdempty ;
wire rd_wrfull ;//wr_fifo
wire wr_rden ;
wire wr_wren ;
wire [15:0] wr_q ;
wire wr_rdempty ;
wire wr_wrfull ;
wire [8:0] wr_rdusedw ;//---------<狀態參數>------------------------------------------------- reg [1:0] state_c ;
reg [1:0] state_n ;localparam IDLE = 2'd0,READ = 2'd1,WRITE = 2'd2,DONE = 2'd3;wire IDLE_2_READ;
wire READ_2_DONE;
wire IDLE_2_WRITE;
wire WRITE_2_DONE;reg [8:0] cnt_burst ;
wire add_cnt_burst ;
wire end_cnt_burst ;//寫地址
reg [23:0] wr_addr ;
wire add_wr_addr;
wire end_wr_addr;
//讀地址
reg [23:0] rd_addr ;
wire add_rd_addr;
wire end_rd_addr;//---------<讀FIFO例化>------------------------------------------------- rd_fifo rd_fifo_inst (.aclr ( ~rst_n ),.data ( avm_readdata ),.rdclk ( clk_out ),.rdreq ( rd_rden ),.wrclk ( clk ),.wrreq ( rd_wren ),.q ( rd_q ),.rdempty ( rd_rdempty ),.wrfull ( rd_wrfull )
);assign rd_wren = ~rd_wrfull && avm_readdatavalid;
assign rd_rden = ~rd_rdempty && tx_done;//---------<寫FIFO例化>------------------------------------------------- wr_fifo wr_fifo_inst (.aclr ( ~rst_n ),.data ( {2{rx_data}} ),.rdclk ( clk ),.rdreq ( wr_rden ),.wrclk ( clk_in ),.wrreq ( wr_wren ),.q ( wr_q ),.rdempty ( wr_rdempty ),.rdusedw ( wr_rdusedw ),.wrfull ( wr_wrfull ));assign wr_wren = ~wr_wrfull && rx_vld;
assign wr_rden = ~wr_rdempty && (state_c == WRITE) && ~avm_waitrequest;//---------<突發計數器>------------------------------------------------- always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_burst <= 'd0;end else if(add_cnt_burst)begin if(end_cnt_burst)begin cnt_burst <= 'd0;endelse begin cnt_burst <= cnt_burst + 1'b1;end end
end assign add_cnt_burst = ((state_c == READ) || (state_c == WRITE)) && ~avm_waitrequest;
assign end_cnt_burst = add_cnt_burst && cnt_burst == (BURST_LENTH-1);//---------<state>------------------------------------------------- //第一段:同步時序描述狀態轉移
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end
end//第二段:組合邏輯判斷狀態轉移條件,描述狀態轉移規律
always @(*) begincase(state_c)IDLE : beginif(IDLE_2_READ)state_n = READ;else if(IDLE_2_WRITE)state_n = WRITE;else state_n = state_c;endREAD : state_n = (READ_2_DONE ) ? DONE : state_c;WRITE : state_n = (WRITE_2_DONE) ? DONE : state_c;DONE : state_n = IDLE;default : state_n = IDLE;endcase
endassign IDLE_2_READ = (state_c == IDLE ) && key_down;
assign READ_2_DONE = (state_c == READ ) && end_cnt_burst;
assign IDLE_2_WRITE = (state_c == IDLE ) && wr_rdusedw >= BURST_LENTH;
assign WRITE_2_DONE = (state_c == WRITE) && end_cnt_burst;//---------<讀寫地址>-------------------------------------------------
//寫地址計數
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginwr_addr <= 'd0;end else if(add_wr_addr)begin if(end_wr_addr)begin wr_addr <= 'd0;endelse begin wr_addr <= wr_addr + 1'b1;end end
end assign add_wr_addr = (state_c == WRITE) && ~avm_waitrequest;
assign end_wr_addr = add_wr_addr && wr_addr == 24'hff_ff_ff-1;//讀地址計數
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_addr <= 'd0;end else if(add_rd_addr)begin if(end_rd_addr)begin rd_addr <= 'd0;endelse begin rd_addr <= rd_addr + 1'b1;end end
end assign add_rd_addr = (state_c == READ) && ~avm_waitrequest;
assign end_rd_addr = add_rd_addr && rd_addr == 24'hff_ff_ff-1;//---------<tx_data tx_vld>-------------------------------------------------
assign tx_data = rd_q [7:0];
assign tx_vld = rd_rden;//---------<狀態判斷>-------------------------------------------------
assign avm_address = (state_c == WRITE) ? {wr_addr[23],wr_addr[21:9],wr_addr[22],wr_addr[8:0]} : {rd_addr[23],rd_addr[21:9],rd_addr[22],rd_addr[8:0]} ;assign avm_writedata = wr_q;
assign avm_read_n = ~(state_c == READ);
assign avm_write_n = ~(state_c == WRITE);
endmodule
其他復用之前的項目