【RK3568+PG2L50H開發板實驗例程】FPGA部分 | ROM、RAM、FIFO 的使用

本原創文章由深圳市小眼睛科技有限公司創作,版權歸本公司所有,如需轉載,需授權并注明出處(www.meyesemi.com)

1.實驗簡介

? 實驗目的:

? ?掌握紫光平臺的 RAM、ROM、FIFO IP 的使用

? 實驗環境: Window11 PDS2022.2-SP6.4

? ?芯片型號: PG2L50H-484

2.實驗原理

? ?不管是 Logos 系列或者是 Logos2 系列,其 IP 配置以及模式和功能均一致,不會像 PLL 那樣有動態配置以及內部反饋選項的選擇等之間的差異,所以是 RAM、ROM、FIFO 是通用的。

2.1. RAM 介紹

? ? ? RAM 即隨機存取存儲器。它可以在運行過程中把數據寫進任意地址,也可以把數據從任意地址中讀出。其作用可以拿來做數據緩存,也可以跨時鐘,也可以存放算法中間的運算結果等。

? ? ? 注意,PDS的IP配置工具中提供兩種不同的RAM,一種是Distributed RAM(分布式RAM) 另一種是 DRM Based RAM,分布式 RAM 用的是 LUT(查找表)資源去構成的 RAM,這種 RAM 會消耗大量 LUT 資源,因此通常在一些比較小的存儲才會用到這種 RAM,以節省 DRM 資源。而 DRM Based RAM 是利用片內的 DRM 資源去構成的 RAM,不占用邏輯資源,而且速度快, 通常設計中均使用 DRM Based RAM。

RAM 分為三種,如下表所示:

RAM類型特點
單端口 RAM只有一個端口可以讀寫,只有一個讀寫口和地址口
偽雙端口 RAM有 wr 和 rd 兩個端口,顧名思義,wr 只能寫,rd 只能讀
真雙端口 RAM提供 A 和 B 兩個端口,兩個端口均可以獨立進行讀寫

注意,當使用真雙端口時,要避免出現同時讀寫同個地址,這會造成寫入失敗,在邏輯設計上需要避開這個情況。

以下給出比較常用的 RAM 的配置作為介紹,通常我們比較常用偽雙端口 RAM 來設計,

如圖所示:

下圖為 IP 配置:

? ? ? 注意,如果勾選 Enable Output Register(輸出寄存),輸出數據會延遲一個時鐘周期。具體每個端口的含義這里參考官方手冊,大家也可以自行查看 IP 手冊,如下圖所示:

? ? ? DRM Resource Type:用于配置所建 RAM IP 核用的是哪種資源,不同芯片型號可選資源是不一樣的,有的是 9K,有的是 18K,有的是 36K,如果沒有特殊情況,直接 AUTO 即可。

2.1.1. RAM 的讀寫時序

? ? ?配置成不同模式的時候,RAM 的讀寫時序是不一樣的,真雙端口和單端口的 RAM 配置均有三種模式,而偽雙端口只有一種。由于真雙端口和單端口的配置是一樣的,這里以真雙端口為例子。

? ? 分為 NORMAL_WRITE( 正 常 模 式 ) 、 TRANSPARENT_WRITE( 直 寫 ) 、 READ_BEFORE_WRITE(讀優先模式)三種模式。

? ? 而偽雙端口不屬于上面三種模式,有它獨特的模式。這幾種模式的差異就在于讀寫時序的不同,接下來,我們來分析讀寫時序。

? ? 以下時序圖均來自官方 IP 手冊,并且均未使能輸出寄存。注意 wr_en 為 1 時表示寫數據, 為 0 表示讀數據。

2.1.1.1.NORMAL_WRITE

? ??在 NORMAL_WRITE 這種模式下,可以看到,當時鐘的上升沿到來,且 clk_en 和 wr_en均為高電平時,就會把數據寫到對應的地址里面,如圖中的 1 時刻。然后看讀數據端口,當wr_en 不為 0 的時候,a_rd_data 一直為 Don’t Care 狀態,而當時鐘上升沿到來,且 clk_en為高電平,wr_en 為低電平時,a_rd_data 輸出當前 a_addr 里的數據,即 Mem(ADDR1)和 ADDR0 里的 D0。

2.1.1.2.READ_BEFORE_WRITE

? ? ?在 READ_BEFORE_WRITE 這種模式下,可以看到在 1 的時刻,時鐘上升沿到來,且clk_en 和 wr_en 均為高電平,D0 寫進了 ADDR0 里面,但是注意看此時的 a_rd_data 和a_addr,可以發現,此時 a_wr_en 并不為 0,可 a_rd_data 還是輸出了上一刻 ADDR0 的數據(因為不是輸出 D0)。之后,a_wr_en 拉低,此時才是讀數據,在 3 時刻,把 ADDR0 的數據讀出來,a_rd_data 才輸出了 D0。

? ? ?所以總結一下,這個模式其實就是進行寫操作時,讀端口會把當前寫的地址的原始數據輸出,因此叫讀優先模式很合情合理對吧,顧名思義,就是我優先把原來的數據讀出來。

2.1.1.3.Transparent_Write

? ??在 Transparent_Write 這種模式下,可以看到在 1 的時刻,時鐘上升沿到來, 且clk_en 和 wr_en 均為高電平,D0 寫進了 ADDR0 里面,但是注意看此時的 a_rd_data 和a_addr,可以發現,此時 a_wr_en 并不為 0,可 a_rd_data 居然直接輸出了 D0,之后 a_wr_en拉低,進入讀狀態,在 2 時刻,再一次把 ADDR0 的數據讀出來,輸出了 D0。

? ??分析總結一下,根據 1 時刻的情況,我們可以得出結論,在這種模式下,當我們進行寫操作時,讀端口會馬上輸出我們寫入的數據。所以叫直寫模式。

2.1.1.4. 偽雙端口的讀寫時序

? ??注意:wr_en 為 1 時是寫操作,為 0 是讀操作。

? ??偽雙端口的讀寫時序與上面三種都不同,我們看圖 8 的時序來分析:

? ??注意看 1 時刻,此時 wr_en 和 wr_clk_en 均為高電平,所以是寫操作,所以 1 時刻就是往地址 ADDR0 里寫入 D0,注意此時的 rd_addr 和 rd_data,可以看到這一時刻 rd_addr 是 ADDR2,然后進行寫操作時,rd_data 同樣輸出了 ADDR2 里的數據,而此時 wr_en 還是高電平。接下來看 2 和 3 時刻,此時 wr_en 為 0,rd_clk_en 是高電平,所以是讀操作,此時分別讀出 ADDR1 和 ADDR0 里的數據,之后 rd_clk_en 變成低電平,讀時鐘無效,可以看到rd_data 保持 D0 輸出。

? ??分析總結一下,主要是 1 時刻,大家可以看到 1 時刻往 ADDR0 寫入了 D0,讀端口卻輸出了 ADDR2 中的數據。仔細觀察可以得出結論:偽雙端口 RAM 在進行寫操作的時候,會把當前讀端口指向的地址的數據輸出。是不是有點像直寫?只不過直寫是輸出寫入的數據,而偽雙端口是輸出讀端口指向的地址的數據。

? ??具體大家可以結合視頻講解。

2.2 ROM 介紹

? ? ?ROM 即只讀存儲器,在程序的運行過程中他只能被讀取,無法被寫入,因此我們應該在初始化的時候就給他配置初值,一般是在生成 IP 的時候通過導入.dat 文件對其進行初值配置。

? ? ?注意,PDS的 IP配置工具中提供兩種不同的 ROM,一種是 Distributed ROM(分布式 ROM)另一種是 DRM Based ROM,分布式 ROM 用的是 LUT(查找表)資源去構成的 ROM,這種 ROM會消耗大量 LUT 資源,因此通常在一些比較小的存儲才會用到這種 RAM,以節省 DRM 資源。而 DRM Based ROM 是利用片內的 DRM 資源去構成的 ROM,不占用邏輯資源,而且速度快,通常設計中均使用 DRM Based ROM。

? ? ?以下給出比較常用的 ROM 的配置作為介紹,由于只能讀,因此其均為單端口 ROM 如圖所示:

? ? 下圖為 IP 配置:

? ??注意,如果勾選 Enable Output Register(輸出寄存),輸出數據會延遲一個時鐘周期。

? ??同時,可以看到 Enable Init 選項是默認勾選的,并且不可取消。

? ??導入的數據的格式只能為二進制或者是十六進制,demo 選擇十六進制。

? ??具體每個端口的含義這里參考官方手冊,大家也可以自行查看 IP 手冊,如圖所示:

? ? ? 可以看到圖中給出的是完整的接口列表,一般我們只需要 addr、rd_data、clk、rst這四個信號即可。

? ? ?以下時序圖均來自官方 IP 手冊,并且均未使能輸出寄存。

2.2.1. ROM 的讀時序

? ? ? 可以看到該時序是非常簡單的,比如在 TI 時刻,當 clk 上升沿到來時,且 clk_en 為高電平時,給出要讀出的地址,rd_data 就會輸出數據,在不勾選輸出使能寄存的情況下,rd_data的輸出會有延遲,具體時間可以從仿真里看到,所以我們在下個時鐘周期的上升沿即 T2 時刻的上升沿才能獲取到 ROM 讀出的值。

? ? ??所以整體時序非常簡單,如果勾選了 clk_en 信號,就要給 clke_en 高電平才能讀數據,如果不勾選 clk_en 信號,就一直根據地址讀取 ROM 數據。

2.3. FIFO 介紹

? ? ?FIFO 即先入先出,在 FPGA 中,FIFO 的作用就是對存儲進來的數據具有一個先入先出特性的一個緩存器,經常用作數據緩存或者進行數據跨時鐘域傳輸。FIFO 和 RAM 最大的區別就是 FIFO 不需要地址,采用的是順序寫入,順序讀出。

? ? ?在紫光的 IP 工具中又分為 Distribute FIFO 和 DRM FIFO,其實就是用不同的資源去構成,前者 Distribute FIFO 也就是分布式 FIFO,使用的是片上的 LUT 資源去構成,而 DRM FIFO 使用的是片上的 DRM 資源去構成,DRM 構成的 FIFO 其性能大于 LUT 資源構成的,不僅容量更大,且可配置更多功能。

? ? ?本章著重介紹 DRM Based FIFO。

? ? ?注意:FIFO 寫滿后禁止繼續寫入數據,否則將會寫溢出。

? ? ?注意:FIFO 讀空后禁止繼續讀數據,否則將會讀溢出。

? ? ?以下給出常用的 FIFO 的配置作為介紹。

? ? ?注意,如果勾選 Enable Output Register(輸出寄存),輸出數據會延遲一個時鐘周期。

? ? ?FIFO Type 有 SYNC 和 ASYNC 兩種,第一種是同步 FIFO,讀寫端口共用一個時鐘和復位,另一種是異步 FIFO,讀寫時鐘和復位均獨立。在平常設計中,比較常用的是異步 FIFO,因為同步 FIFO 和異步 FIFO 的讀寫時序一模一樣,只有讀寫端口的時鐘復位有差異,當異步 FIFO 的讀寫端口使用相同的時鐘和復位,此時異步 FIFO 和同步 FIFO 基本是一致的。

? ? ?Reset Type 也可以選擇 SYNC 和 ASYNC 兩種,SYNC 模式下需要時鐘的上升沿采樣到復位有效才會復位,而在 ASYNC 模式下,復位一旦有,FIFO 立即復位。

? ? ?其余端口說明引用官方 IP 手冊,如圖所示:

? ? ?其中 rd_water_level 和 wr_water_level 分別代表”可讀的數據量”和”已寫入的數據量”,其含義與 Xilinx 的 FIFO 的 wr_data_count 和 rd_data_count 是一致的。

? ? ?當我們將 Enable Almost Full Water Level 和 Enable Almost Empty Water Level 勾選上,才能看到 rd_water_level 和 wr_water_level,而下面的 Almost Full Numbers 的設置是表示當寫入 1020 個數據時,Almost Full 信號就會拉高,Almost Empty Numbers 的設置表示當可讀數據剩下 4 個時 Almost Empty 信號就會拉高。

? ? ?同時 FIFO 支持混合位寬,例如寫端口 16bit,讀端口 8bit。如果寫入 16’h0102,那么讀出來會是 8’h02,8’h01,會先讀出低位。

? ? ?如果寫端口 8bit,讀端口 16bit。當寫入 8’h01,8’h02 時,讀出來是 16’h0201,先寫入的數據存放在低位。

2.3.1.FIFO的讀寫時序

? ? ?因為同步 FIFO 和異步 FIFO 的讀寫時序一致,這里用異步 FIFO 的讀寫時序圖來做介紹。

? ? ?注意:復位時高電平有效。讀出數據均未勾選 Enable Output Register(輸出寄存)。

2.3.1.1.FIFO 未滿時的寫時序

? ? ?可以看到在 1 時刻,復位信號時低電平,處于工作狀態,此時在 wr_clk 的上升沿且 wr_en 為高電平時將數據 D0 寫入 FIFO,wr_water_level 也從 0 變 1,表示已經寫入了一個數據,此時注意看讀端口的 empty 信號,在 3 時刻 empty 信號從高變低,意味著讀端口已經有數據可以讀了,FIFO不再為空,而注意看,rd_clk和 wr_clk是不一樣的,從 1寫入到 3時刻 empty 拉低時,經過了 3 個 rd_clk。

? ? ?所以這里我們可以得出結論:rd_water_level 要滯后 wr_water_level 三個 rd_clk。

2.3.1.2.FIFO 將滿時的寫時序

? ? ?將滿時主要分析 full 和 almost_full 信號。假設 Almost Full Numbers 設置為 N-2,在 1時刻,此時已經寫入了 N-6 個數據,意味著再寫 6 個數據 FIFO 就滿了,從 1 時刻到 2 時刻一共寫入了 4 個數據,因此當 wr_water_level 變成 N-2 時,滿足條件,可以看到 Almost Full信號拉高,再寫兩個數據 FIFO 就滿了,所以再經過兩個時鐘周期后,Full 信號拉高。

2.3.1.3.FIFO 在滿狀態下的讀時序

? ? ?在滿狀態下,FIFO 已經有 N 個數據了,此時在 1 狀態下,rd_clk 的上升沿,且 rd_en 為 高 電 平 時 ,此 時 從 FIFO 里 讀 出 數 據 (數 據 的 輸 出 有 延 時 ,仿 真 中 延 時 0.2ns) 。此時 rd_water_level 變成 N-1,rd_data 輸出 D0。然后看 2 時刻,full 信號拉低,此時可以看以下,在 1 時刻到 2 時刻期間一共經過了 3 個 wr_clk 寫端口才能判斷到此時數據量已經不為滿。

? ? ?所以我們可以得出結論,wr_water_level 要滯后 rd_water_level 三個 wr_clk。

2.3.1.4.FIFO 將空時的讀時序

? ? ?在 1 時刻,可讀的數據量剩下 4,假設 Almost Empty Number 設為 2,在 1 時刻和 2 時刻分別讀出了兩個數據,所以在 2 時刻下,可讀數據量剩下兩個,達到 Almost Empty Number 觸發條件,因此 almost_empty 信號拉高,再過兩個時鐘周期,即再讀兩個數據,FIFO 將變成空狀態,也就是狀態 3,此時 empty 信號拉高。

2.4. 接口列表

? ?該部分介紹每個頂層模塊的接口。

ram_test_top.v

rom_test_top.v

fifo_test_top.v

3.代碼仿真說明

? ? ?本次的頂層模塊實際就是例化 IP,然后把端口引出而已,主要代碼都在 testbench 里面,所以我們直接介紹仿真代碼。

3.1.?RAM 仿真測試
`timescale 1ns/1nsmodule ram_test_tb();reg sys_clk;reg rd_clk;reg rst_n;reg rw_en; //讀寫使能信號reg [7:0] wr_data;reg [4:0] wr_addr;reg [4:0] rd_addr;wire [7:0] rd_data;reg [1:0] state;initial beginrst_n <= 1'd0;sys_clk <= 1'd0;rd_clk <= 1'd0;#20rst_n <= 1'd1;end//讀寫控制always@(posedge sys_clk or negedge rst_n) beginif(!rst_n) beginstate <= 2'd0;wr_data <= 8'd0;rw_en <= 1'd0;wr_addr <= 8'd0;rd_addr <= 8'd0;endelse begincase(state)2'd0: beginrw_en <= 1'd1;state <= 2'd1;end2'd1: beginif(wr_addr == 5'd31) beginrw_en <= 1'd0;state <= 2'd2;wr_data <= 8'd0;wr_addr <= 5'd0;rd_addr <= 5'd0;endelse beginstate <= 2'd1;wr_data <= wr_data+1'b1;rd_addr <= rd_addr+1'b1;wr_addr <= wr_addr+1'b1;endend2'd2: beginif(rd_addr == 5'd31) beginstate <= 2'd3;rd_addr <= 5'd0;endelse beginstate <= 2'd2;rd_addr <= rd_addr+1'b1;endend2'd3: beginstate <= 2'd0;enddefault: state <= 2'd0;endcaseendend//50MHZalways #10 sys_clk = ~sys_clk;GTP_GRS GRS_INST(.GRS_N(1'b1));ram_test_top u_ram_test_top(.wr_clk (sys_clk),.rd_clk (sys_clk),.rst_n (rst_n),.rw_en (rw_en),.wr_addr (wr_addr),.rd_addr (rd_addr),.wr_data (wr_data),.rd_data (rd_data));
endmodule

? ? ?涉及到 tb 的一些基礎操作這里就不再詳細講解,只關注重點邏輯部分。從代碼的 27 行到 80 行是 ram 的讀寫控制狀態機。主要用來控制讀寫地址的生成和使能以及寫入的數據。這里只講解主要實現的功能,首先代碼的 38-42 行,也就是 state=0 的時候,拉高 rw_en,并跳轉到狀態 1,此時進入寫操作(沒有使能 clk_en,可以不管),下個時鐘周期開始寫入數據(注意是時序邏輯,邊沿采樣,所以是下個時鐘周期才開始寫數據),即 state=1 的時候是一直在往 ram 里面寫數據,在代碼的 44 到 60 行就是寫操作了,可以看到,當 wr_addr 不等于 31 的時候,wr_data 和 wr_addr 不斷加 1(rd_addr 這里+1,可以看視頻講解,主要為了驗證偽雙端口的時序),當 wr_addr 等于 31 的時候,在下個時鐘周期把數據清 0,狀態跳轉,在當前時鐘周期下還會再往地址 31 里面寫入數據,所以在該時鐘周期,一共寫入了 32 個數據(從地址 0 寫到地址 31)。即狀態 1 完成寫入 32 個數據后跳轉到 state=2 的邏輯。代碼的 61-72 行,也就是 state=2 的時候,在每個周期的上升沿讓 rd_addr 不斷累加,直到 rd_addr=31 的時候,在下個時鐘周期清空地址并讓狀態跳轉的操作,而在當前時鐘周期會繼續把地址 31 的數據讀出來,完成讀取地址 0-31 的數據,一共 32 個數據,所以該狀態主要完成讀取 32 個數據,然后在下個時鐘周期就跳轉到 state=3。state=3 可以看到其主要作用就是等待一個時鐘周期,然后跳轉回去 state=0 下,起到一個延時作用。

?上圖為寫數據的波形,數據從 0 開始遞增到 31,地址也是從 0 到 31。

上圖為讀數據波形,從地址 0-31 讀出了 0-31 個數據。

? ? ? 具體波形大家可以看視頻仿真,或者自己嘗試仿真,根據波形來看代碼。因為這里是時序邏輯,所以如果是初學者,純看文字可能會對 rd_addr=31 這一時刻還會再讀一個數據感到疑惑,建議直接仿真,或者觀看視頻講解的仿真部分,可以幫助快速理解。

? ? ? 可以總結出一句話就是時序邏輯的賦值總在下一個時鐘周期才生效。所以在rd_addr=31 時執行的操作要在下一個時鐘周期才會被采樣生效。所以當前時鐘還是會再從RAM 讀出一個數據。

? ? ??仿真代碼的講解到此結束,大家要注意時序邏輯的特點,具體的內容請看視頻講解。

3.2.?ROM 仿真測試
`timescale 1ns/1nsmodule rom_test_tb();reg sys_clk;reg rst_n;reg [9:0] rd_addr;wire [63:0] rd_data;initial beginrst_n <= 1'd0;sys_clk <= 1'd0;#20rst_n <= 1'd1;end//50MHZalways #10 sys_clk = ~sys_clk;GTP_GRS GRS_INST(.GRS_N(1'b1));always@(posedge sys_clk or negedge rst_n) beginif(!rst_n)rd_addr <= 10'd0;elserd_addr <= #2 rd_addr + 1'b1;endrom_test_top u_rom_test_top(.rd_clk (sys_clk),.rst_n (rst_n),.rd_addr (rd_addr),.rd_data (rd_data));endmodule

? ? ? 代碼 31-36 行例化了 ROM 的頂層模塊,該模塊里面其實就是調用了 ROM IP,然后把信號引出端口,沒有任何邏輯操作。

? ? ??代碼 24-29 行通過一個 always 塊不斷生成地址,任何給到 ROM IP,將數據讀出,由于沒勾選 clk_en 信號,所以數據在 ROM 復位完成后就會不斷讀出。所以并沒有復雜的邏輯,就是讓地址從 0 不斷累加,把數據讀出。

? ? ??上圖為讀出數據的波形圖,可以看到讀出的數據和 dat 文件里的數據一致。

? ?仿真代碼的講解到此結束,大家要注意時序邏輯的特點,具體的內容請看視頻講解。

3.3.?FIFO 仿真測試
`timescale 1ns/1nsmodule fifo_test_tb();reg sys_clk;reg rst_n;reg [7:0] wr_data;reg wr_en;reg rd_en;reg rd_state; //讀狀態reg wr_state;wire [7:0] rd_data;reg [7:0] rd_cnt;wire [7:0] rd_water_level;wire [7:0] wr_water_level;initial beginrst_n <= 1'd0;sys_clk <= 1'd0;#20rst_n <= 1'd1;endalways #10 sys_clk = ~sys_clk; //50MHZalways@(posedge sys_clk or negedge rst_n) beginif(!rst_n) beginwr_state <= 1'd0;wr_en <= 1'd0;wr_data <= 8'd0;endelse begincase(wr_state)1'd0: if(wr_water_level == 127) //128 個數據beginwr_en <= #2 1'd0;wr_data <= #2 8'd0;wr_state <= #2 1'd1;endelsebeginwr_en <= #2 1'd1;wr_data <= #2 wr_data+1'b1;wr_state <= #2 1'd0;end1'd1: if(rd_cnt == 127)wr_state <= #2 1'd0;default: wr_state <= 1'd0;endcaseendendalways@(posedge sys_clk or negedge rst_n) beginif(!rst_n) beginrd_state <= 1'd0;rd_en <= 1'd0;rd_cnt <= 8'd0;endelse begincase(rd_state)1'd0: if(rd_water_level >= 8'd128) //等待 128 個數據beginrd_state <= #2 1'd1;rd_en <= #2 1'd1;endelsebeginrd_cnt <= #2 8'd0;rd_state <= #2 1'd0;end1'd1: beginrd_cnt <= #2 rd_cnt + 1'b1;if(rd_cnt == 127)beginrd_en <= #2 1'd0;rd_state <= #2 1'd0;endenddefault: rd_state <= 1'd0;endcaseendendGTP_GRS GRS_INST(.GRS_N(1'b1));fifo_test_top u_fifo_test_top(.sys_clk (sys_clk),.rst_n (rst_n),.wr_data (wr_data),.wr_en (wr_en),.rd_en (rd_en),.wr_water_level (wr_water_level),.rd_water_level (rd_water_level),.rd_data (rd_data));
endmodule

? ? ? 涉及到 tb 的一些基礎操作這里就不再詳細講解,只關注重點邏輯部分。整個設計分為讀寫兩個狀態的控制。分別完成了寫入 128 個數據,和讀出 128 個數據,由于 FIFO 不需要地址,所以只需要產生使能信號即可。

? ? ??首先看寫狀態,在 wr_state=0 時,拉高寫使能,并讓 wr_data 不斷累加,往 FIFO 里面寫數據,當 wr_water_level=127 的時候,拉低寫使能,寫數據置 0,寫狀態跳轉到 1,注意此時還會再寫入一個數據,所以到此一個寫入了 128 個數據。至于拉低寫使能,寫數據置 0,寫狀態跳轉到 1 這些操作將在下一個時鐘周期才會被采樣生效。之后,在 wr_state=1 時,不斷等待 rd_cnt,該條件就是判斷當讀出 128 個數據的時候,wr_state 跳轉到 0 狀態。

? ? ??接下來看讀狀態,在 rd_state=0 的時候,一旦可讀的數據量超過 128 個(包括 128),狀態跳轉到 rd_state=1 下,然后開始讀出數據,同時在 rd_state=1 下用變量 rd_cnt 對我們的讀出數據也進行計數,rd_cnt 從 0 開始計數,當 rd_cnt=127 的時候會再往 FIFO 讀出一個數據,所以此時就一共讀出了 128 個數據,下一個時鐘周期 rd_en 和 rd_state 都將置 0。

寫數據的波形如上所示,一共寫入 128 個數據,從 1 寫到 128。

讀數據的波形如上所示,一共讀出 128 個數據,從 1 讀到 128。

? ? ? 具體波形大家可以看視頻仿真,或者自己嘗試仿真,根據波形來看代碼。因為這里是時序邏輯,所以如果是初學者,純看文字可能會對 rd_cnt=127 這一時刻還會再讀一個數據感到疑惑,建議直接仿真,或者觀看視頻講解的仿真部分,可以幫助快速理解。

? ? ??可以總結出一句話就是時序邏輯的賦值總在下一個時鐘周期才生效,所以在 rd_cnt=127 時執行的操作要在下一個時鐘周期才會被采樣生效。所以當前時鐘 rd_en 還是 為 1,會再從 FIFO 讀出一個數據。

? ? ??仿真代碼的講解到此結束,具體的內容請看視頻講解。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/90646.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/90646.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/90646.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

力扣-21.合并兩個有序鏈表

題目鏈接 21.合并兩個有序鏈表 class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {ListNode p1 list1;ListNode p2 list2;ListNode p new ListNode(0);ListNode cur p;while (p1 ! null && p2 ! null) {if (p1.val > p2.val) …

MoE混合專家模型:千億參數的高效推理引擎與架構革命

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; 從稀疏激活到多模態協同的智能計算范式 &#x1f9e9; 一、核心思想與…

【論文筆記】BlockGaussian:巧妙解決大規模場景重建中的偽影問題

論文地址&#xff1a;https://arxiv.org/pdf/2504.09048 大規模場景的重建方法不僅僅對于高空航拍數據有效&#xff0c;而且對于地面大中場景也有增強效果&#xff0c;故專門來學習一下這一方向的知識。感謝作者大佬們的great work。 Abstract 三維高斯潑濺&#xff08;3DGS…

網絡眾籌項目數據庫(2014-2024.11)

1727網絡眾籌項目數據庫&#xff08;2014-2024.11&#xff09;數據簡介作為新興互聯網融資模式&#xff0c;眾籌已成為越來越多創業者和中小企業獲取資金的渠道&#xff0c;但眾籌項目一直面臨融資成功率低的困難&#xff0c;成功融資的項目在許多平臺上占比不足五成。而目前對…

k8s新增jupyter服務

k8s新增服務 常用命令 kubectl apply -f xxxxxx.yaml # 部署資源&#xff0c;順序&#xff1a;namespace -> pvc -> deployment -> servicekubectl create namespace jupyter # 創建namespacekubectl get namespaces # 查看nskubectl get pods -n jupyter # 查看p…

結構化數據、非結構化數據區別

一、核心定義結構化數據&#xff1a;指具有固定格式、可直接用二維表&#xff08;如數據庫表&#xff09;表示的數據&#xff0c;其字段&#xff08;列&#xff09;定義明確&#xff0c;數據之間的關系清晰。例如&#xff1a;Excel 表格中的數據、關系型數據庫&#xff08;MySQ…

Linux修煉:基礎指令

Hello大家好&#xff01;很高興我們又見面啦&#xff01;給生活添點passion&#xff0c;開始今天的編程之路&#xff01; 我的博客&#xff1a;<但凡. 我的專欄&#xff1a;《編程之路》、《數據結構與算法之美》、《題海拾貝》、《C修煉之路》、《Linux修煉&#xff1a;終端…

【Linux網絡】深入理解HTTP/HTTPS協議:原理、實現與加密機制全面解析

協議是通信雙方必須遵守的規則&#xff0c;確保數據能夠正確傳輸和解析&#xff0c;它規定了數據格式、傳輸順序、錯誤處理等細節。應用層的協議一般都是我們自己進行定義的&#xff0c;但是有很多程序員前輩已經寫出來了很哇塞的協議&#xff0c;我們直接進行學習和使用即可HT…

淺嘗 Spring AI【使用超級簡單~】

一直想要體驗下 Spring AI&#xff0c;最近自己的一個工具有這個需求&#xff0c;所以這里準備使用下。其實使用起來超級簡單。 1.IDEA 新建 Spring項目 1&#xff09;這里可以根據自己的喜好選擇 項目名、jdk版本等 2&#xff09;這里選擇 在ai中選擇 openAI 即可。然后我另…

DDL期間TDSQL異常會話查詢造成數據庫主備切換

問題描述&#xff1a;7*24聯機交易系統&#xff0c;傍晚時分&#xff0c;從客戶端后臺對3千萬行的大表執行縮短varchar類型字段長度的ddl語句&#xff0c;執行期間&#xff0c;為了查看ddl進度&#xff0c;從TDSQL-MySQL赤兔前端頁面點擊異常會話查詢&#xff0c;之后數據庫卡住…

弧焊機器人氣體全方位節能指南

氬弧焊&#xff08;TIG焊接&#xff09;作為其中一種高效且精密的技術&#xff0c;憑借其穩定性和高質量的焊接效果&#xff0c;在航空航天、汽車制造、船舶建造以及石油化工等領域占據了不可或缺的地位。氬弧焊通過使用惰性氣體&#xff08;如氬氣&#xff09;保護電弧和熔池&…

數據清洗(ETL/ELT)原理與工具選擇指南:企業數字化轉型的核心引擎?

目錄 一、數據清洗&#xff08;ETL/ELT&#xff09;到底在干啥&#xff1f; 1.揪出并處理異常值 2.把缺失的數據補上&#xff08;或處理好&#xff09; 3.數據轉換與標準化 4.一致性校驗 二、工具怎么選&#xff1f;看菜吃飯&#xff0c;量體裁衣 1.數據量不大、要求不高…

阿里云服務器,CentOS7.9上安裝YApi 接口管理平臺

目錄 1.node安裝 1.1下載node,解壓 1.2 部署bin文件 1.3 安裝mongodb 2.啟動yapi 2.1 前置命令 2.2 啟動服務 3.利用pm2方便服務管理維護 3.1.安裝pm2 3.2 常用 PM2 命令 4.常見問題 4.1. 確認 MongoDB 是否安裝 4.2. 安裝 MongoDB&#xff08;若未安裝&#xff…

阿里云錯題集分享

有最近想要考試阿里云的可以私信我 &#xff0c;一起加油錯題集1.在使用阿里云的負載均衡SLB實例時&#xff0c;做了如下健康檢查的配置:成功響應和超時響應時間均為1秒&#xff0c;健康檢查間隔為2秒&#xff0c;不健康閾值為3&#xff0c;健康閾值為3。即對于確認一個云服務器…

Android 12 - 部分相機橫屏顯示方案

1.相機過渡界面方向旋轉 Android 10 - 相機過渡界面默認角度 同A10 有些區別&#xff0c;再次增加記錄修改。 這個文件沒有修改&#xff0c;只是說明 src/com/android/camera/CameraActivity.javaprivate void freezeScreenCommon(boolean async) {long startTime System.…

Operation Blackout 2025 Phantom Check hayabusa+ControlSet001+VirtualBox

QAQA攻擊者使用哪個 WMI 類來檢索型號和制造商信息以進行虛擬化檢測&#xff1f;Win32_ComputerSystem攻擊者執行了哪個 WMI 查詢來檢索計算機的當前溫度值&#xff1f;SELECT CurrentTemperature FROM MSAcpi_ThermalZoneTemperature攻擊者加載了 PowerShell 腳本以檢測虛擬化…

《O-PAS?標準的安全方法》白皮書:為工業自動化系統筑起安全防線

The Open Group 最新白皮書《O-PAS?標準的安全方法》重磅發布&#xff0c;為流程工業在邁向開放架構與多供應商互操作的過程中&#xff0c;指明了安全實踐的方向。O-PAS?標準的安全方法ABOUT PUBLICATION亮點一&#xff1a;首次系統闡釋 O-PAS? 標準安全方法與 IEC/ISA 6244…

UML 圖類型全解析:結構圖與行為圖分類詳解

作為軟件架構的核心建模語言&#xff0c;UML&#xff08;統一建模語言&#xff09;通過14種標準圖表提供系統多維度視角。這些圖表分為結構圖&#xff08;靜態模型&#xff09; 和 行為圖&#xff08;動態模型&#xff09; 兩大類&#xff0c;覆蓋從需求到實現的完整生命周期。…

lodash不支持 Tree Shaking 而 lodash-es可以

lodash 無法有效支持 Tree Shaking 而 lodash-es 可以&#xff0c;核心區別在于?模塊規范、文件結構和靜態分析兼容性?。以下是具體原因分析&#xff1a; ?? 一、模塊規范差異&#xff08;核心原因&#xff09; lodash&#xff08;CommonJS 規范&#xff09;? 使用 requir…

java+vue+SpringBoo高校實習信息發布網站(程序+數據庫+報告+部署教程+答辯指導)

源代碼數據庫LW文檔&#xff08;1萬字以上&#xff09;開題報告答辯稿ppt部署教程代碼講解代碼時間修改工具 技術實現 開發語言&#xff1a;后端&#xff1a;Java 前端&#xff1a;vue框架&#xff1a;springboot數據庫&#xff1a;mysql 開發工具 JDK版本&#xff1a;JDK1.8 數…