【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十五:FIFO储存模块(同步)

简介: 实验十五:FIFO储存模块(同步) 笔者虽然在实验十四曾解释储存模块,而且也演示奇怪的家伙,但是实验十四只是一场游戏而已。至于实验十五,笔者会稍微严肃一点,手动建立有规格的储存模块,即同步FIFO。那些看过《时序篇》的同学一定对同步FIFO不会觉得陌生吧?因为笔者曾在《时序篇》建立基于移位寄存器的同步FIFO。

实验十五:FIFO储存模块(同步)

笔者虽然在实验十四曾解释储存模块,而且也演示奇怪的家伙,但是实验十四只是一场游戏而已。至于实验十五,笔者会稍微严肃一点,手动建立有规格的储存模块,即同步FIFO。那些看过《时序篇》的同学一定对同步FIFO不会觉得陌生吧?因为笔者曾在《时序篇》建立基于移位寄存器的同步FIFO。不过那种同步FIFO只是用来学习的玩具而已。因此,这回笔者可要认真了!

事实告诉笔者,同步FIFO的利用率远胜其它储存模块,几乎所有接口模块都会出现它的身影。早期的时候,笔者都会利用官方准备的同步FIFO(官方插件模块),大伙都知道官方插件模块都非常傲娇,心意(内容)不仅不容易看透,而且信号也不容易捉摸,最重要是无法随心所欲摆布它们。与其跪下向它求救,笔者还不如创建自己专属的同步FIFO。

故名思议,“同步”表示相同频率的时钟源,“FIFO”表示先进先出的意思。FIFO的用意一般都是缓冲数据,另模块独立,让模块回避调用的束缚。同步FIFO是RAM的亚种,它基于RAM,再加上先进先出的机制,学习同步FIFO就是学习如何建立先进先出的机制。

clip_image002

图15.1 同步FIFO建模图(常规)。

常规上,同步FIFO的建模图如图15.1所示,左边有写入请求 ReqW,写入数据 DataW,还有写满标示Full。换之,右边则有读出请求ReqR,读出数据DataR,还有读空标示 Empty。写入方面,ReqW必须拉高DataW才能写入,一旦FIFO写满,那么Full就会拉高。至于读出方面,ReqR 必须拉高,数据才能经由DataR读出,一旦FIFO读空,Empty就会拉高。不过图15.1可以稍微更动一下,另它更加接近低级建模II的形象。

clip_image004

图15.2 同步FIFO建模图(低级建模II)。

如图15.2所示,Req× 改为沟通信号 En,其中En[1] 表示写入使能, En[0]表示读出使能。Data× 改为数据信号Data,iData为写入数据,oData为读出数据。Full与Empty 则改为状态信号 Tag[1] 与 Tag[0] 。

1. module fifo_savemod

2. ( 

3. input CLOCK, RESET,

4. input [1:0]iEn,

5. input [3:0]iData,

6. ouptut [3:0]oData

7. output [1:0]oTag

8. );

9. ......

10. assign oTag[1] = ...; // Full

11. assign oTag[0] = ...; // Empty

12.

13. endmodule

代码15.1

同步FIFO大致的外皮如代码15.1所示,第3~7行是相关的出入端声明,第10~11行则是相关的输出驱动声明。理解这些以后,接下来我们要学习先进先出这个机制。

clip_image006

图15.3 读空状态。

假设笔者建立位宽为4,深度为4的ram,然后又建立位宽为3的写指针WP与读指正RP。同学一定会好奇道,既然ram只有4个深度,那么指针只要2位宽(22 = 4)即不是可以访问所有深度呢?话虽如此,为了利用指正表示写满与读空状态,指针必须多出一位 ... 因此,指针的最高位常常也被称为方向位。

如图15.3所示,一开始的时候,写指针与读指针同样指向地址0,而且ram里边也是空空如也,为此读空状态“叮咚”亮着红灯。为此,我们可以暂时这样表示读空的逻辑关系:

Empty = (WP == RP);

clip_image008

图15.4 写入中①。

当火车开动以后,首先数据 4’hA 写入地址0,然后写指针从原来的 3’b0_00 递增为 3’b0_01,并且指向地址1。此刻,ram再也不是空空入也,所示读空状态消除红灯,结果如图15.4 所示。

clip_image010

图15.5 写入中②。

紧接着,数据 4’hB 写入地址1,然后写指针从原来的 3’b0_01 递增为 3’b0_10,并且指向地址2,结果如图15.5所示。

clip_image012

图15.6写入中③

然后,数据 4’hC 写入地址2,然后写指针从原来的 3’b0_10 递增为 3’b0_11,并且指向地址3,结果如图15.6所示。

clip_image014

图15.7 写满状态。

接着,数据 4’hD 写入地址3,然后写指针从原来的 3’b0_11 递增为 3’b1_00,并且重新指向地址0。此刻写指针的最高位为1,这表示写指针已经绕弯ram一圈又回来原点,反之读指针从刚才开始一动也不动,结果最高为0 ... 所以我们可以说写指针与读指针目前处于不同的方向。在此ram已经写满,所以写满状态便“叮咚”亮红灯,结果如图15.6所示。写满状态的逻辑关系则可以这样表示:

FULL = (WP[2] ^ RP[2] && WP[1:0] == RP[1:0]);

clip_image016

图15.8 读出中①。

从现在开始,另一头火车才开始走动 ... 首先数据4’hA从地址0读出来,读指针也从原本的 3’b0_00 递增为 3’b0_01,并且指向地址1。此刻ram再也不是吃饱饱的状态,所以写满状态被消除红灯,结果如图15.8所示。

clip_image018

图15.9 读出中②。

接下来,数据4’hB 从地址1哪里读出,读指针也从原本的 3’b0_01 递增为 3’b0_10,并且指向地址2,结果如图15.9所示。

clip_image020

图15.10 读出中③。

随之,数据4’hC 从地址2哪里读出,读指针也从原本的 3’b0_10 递增为 3’b0_11,并且指向地址3,结果如图15.10所示。

clip_image022

图15.11读空状态。

最后,数据4’hD 从地址3哪里读出,读指针也从原本的 3’b0_11 递增为 3’b1_00,并且重新指向地址0。当读指针绕弯一圈又回到原点的时候,读者的最高位也成为值1,换句话说 ... 此刻的读指针与写指针也处于同样的位置。同一个时候,ram也是空空如也,所以读空状态便“叮咚”亮起红灯,结果如图15.11所示。为此,读空状态的逻辑关系可以这样表示:

Empty = (WP == RP);

总结而言,当我们设置N位位宽的时候,读写指针的位宽便是 N + 1。此外,读空状态为写指针等价读指针。反之,写满状态是两个指针方向一致(异或状态),然后地址一致。理解先进先出的机制以后,接下来我们便可以填充一下FIFO储存模块的内容。

1. module fifo_savemod

2. (

3. input CLOCK, RESET, 

4. input [1:0]iEn,

5. input [3:0]iData,

6. output [3:0]oData,

7. output [1:0]oTag

8. );

9. reg [3:0] RAM [3:0]; 

10. reg [3:0]D1;

11. reg [2:0]C1,C2; // N+1

12.

13. always @ ( posedge CLOCK or negedge RESET )

14. if( !RESET )

15. begin

16. C1 <= 3'd0;

17. end

18. else if( iEn[1] ) 

19. begin

20. RAM[ C1[1:0] ] <= iData; 

21. C1 <= C1 + 1'b1; 

22. end

23.

24. always @ ( posedge CLOCK or negedge RESET )

25. if( !RESET )

26. begin

27. D1 <= 4'd0;

28. C2 <= 3'd0;

29. end

30. else if( iEn[0] )

31. begin 

32. D1 <= RAM[ C2[1:0] ]; 

33. C2 <= C2 + 1'b1; 

34. end

35.

36. assign oData = D1;

37. assign oTag[1] = ( C1[2]^C2[2] & C1[1:0] == C2[1:0] ); // Full

38. assign oTag[0] = ( C1 == C2 ); // Empty

39.

40. endmodule

代码15.2

笔者在第9~11行创建相关的寄存器,C1取代WP,C2取代RP。第13~22行是写操作,内容非常单纯,即 iEn[1] 拉高便将 iData 写入 C1[1:0] 指定的地方,然后C1递增。

第24~34行是读操作,内容也是一样单纯,iEn[0] 拉高便将 C2[1:0] 指定的数据暂存至 D1,随后C2递增,最后由D驱动oData。第37~38行是写满状态与读空状态的逻辑关系。

clip_image024

图15.12 调用FIFO储存模块。

创建同步FIFO基本上没有什么难度,但是调用FIFO倒是一件难题。如图15.12所示,笔者建立一支核心操作尝试调用 FIFO储存模块,至于核心操作的内容如代码15.3所示:

1. case( i ) // Core

2. 0:

3. if( iTag[1]! ) begin oEn[1] <= 1’b1; oData <= 4’hA; i <= i + 1’b1; end

4. 1:

5. if( iTag[1]! ) begin oEn[1] <= 1’b1; oData <= 4’hB; i <= i + 1’b1; end

6. 2:

7. if( iTag[1]! ) begin oEn[1] <= 1’b1; oData <= 4’hC; i <= i + 1’b1; end

8. 3:

9. if( iTag[1]! ) begin oEn[1] <= 1’b1; oData <= 4’hD; i <= i + 1’b1; end

10. 4:

11. begin oEn[1] <= 1’b0; i <= i + 1’b1; end

12. 5:

13. if( iTag[0]! ) begin oEn[0] <= 1’b1; i <= i + 1’b1; end

14. 6:

15. if( iTag[0]! ) begin oEn[0] <= 1’b1; i <= i + 1’b1; end

16. 7:

17. if( iTag[0]! ) begin oEn[0] <= 1’b1; i <= i + 1’b1; end

18. 8:

19. if( iTag[0]! ) begin oEn[0] <= 1’b1; i <= i + 1’b1; end

20. 9:

21. begin oEn[0] <= 1’b0; i <= i + 1’b1; end

22. endcase

代码15.3

如代码15.3所示,步骤0~3用来一边检测 Tag[1] 是否为高,一边向储存模块写入数据 4’hA~4‘hD,步骤4则用来拉低 oEn[1] 并且歇息一下。步骤5~8用来一边检测 Tag[0] 是否为高,一边从储存模块哪里读出数据,步骤9则用来拉低 oEn[0]并且偷懒一下。

clip_image026

图15.13 读写FIFO储存模块的理想时序图。

图15.13是代码15.3所生产的理想时序图,同时也是核心操作作为视角的时序,至于C1~C2是FIFO储存模块作为视角的时序。各个视角的时序过程如下:

核心操作视角:

l T0,isTag[1]为低(即时值),拉高oEn[1](未来值),并且发送数据 4’hA(未来值)。

l T1,isTag[1]为低(即时值),拉高oEn[1](未来值),并且发送数据 4’hB(未来值)。

l T2,isTag[1]为低(即时值),拉高oEn[1](未来值),并且发送数据 4’hC(未来值)。

l T3,isTag[1]为低(即时值),拉高oEn[1](未来值),并且发送数据 4’hD(未来值)。

l T4,isTag[1]为高(即时值),拉低oEn[1](未来值)。

l T5,isTag[0]为低(即时值),拉高oEn[1](未来值)。

l T6,isTag[0]为低(即时值),拉高oEn[1](未来值),数据4’hA读出(过去值)。

l T7,isTag[0]为低(即时值),拉高oEn[1](未来值),数据4’hB读出(过去值)。

l T8,isTag[0]为低(即时值),拉高oEn[1](未来值),数据4’hC读出(过去值)。

l T9,isTag[0]为高(即时值),拉低oEn[1](未来值),数据4’hD读出(过去值)。

l T10,isTag[0]为高(即时值)。

FIFO储存模块视角:

l T0,oEn[1]为低(过去值)。C1等价C2为读空状态,iTag[0]拉高(即时值)。

l T1,oEn[1]为高(过去值),读取数据4’hA(过去值),递增C1。C1不等价C2,iTag[0]拉低(即时值)。

l T2,oEn[1]为高(过去值),读取数据4’hB(过去值),递增C1。

l T3,oEn[1]为高(过去值),读取数据4’hC(过去值),递增C1。

l T4,oEn[1]为高(过去值),读取数据4’hA(过去值),递增C1。C1等价C2为写满状态,iTag[1]拉高(即时值)。

l T5,oEn[1]为低(过去值)。

l T6,oEn[0]为高(过去值),读出数据4’hA(未来值),递增C2。C1不等价C2,isTag[1]拉低(即时值)。

l T7,oEn[0]为高(过去值),读出数据4’hB(未来值),递增C2。

l T8,oEn[0]为高(过去值),读出数据4’hC(未来值),递增C2。

l T9,oEn[0]为高(过去值),读出数据4’hD(未来值),递增C2。C1等价C2为读空状态,isTag[0]拉高(即时值)。

l T10,oEn[0]为低(过去值)。

读者是不是一边浏览一边捏蛋蛋呢?什么过去值,又什么未来值,又又什么即时值的 ... 没错,同步FIFO的设计原理虽然简单,但是时序解读却让人泪流满面。因为同步FIFO夹杂两种时序表现——时间点事件还有即时事件。如图15.13 所示,除了 iTag 信号是触发即时事件以外,所有信号都是触发时间点事件。读过《时序篇》或者《工具篇II》的朋友一定知晓,即时值不仅比过去值优先,而且即时值也会无视时钟。

好奇的同学可能困惑道:“为什么iTag不能设计成为时间点事件呢?”。笔者曾在《时序篇》建立基于移位寄存器的FIFO,其中iTag就是设计成为时间点事件,结果FIFO的写满状态或者读空状态都来不及反馈,因此发生调用上的混乱。

clip_image028

图15.14 读写FIFO储存模块的即时事件。

为了理解重点,首先让我们来焦距写数据的部分。如图15.14所示,关键的地方就是发生在T4——这只时钟沿。T4之际,FIFO储存模块读取oEn[1]的过去值,C1也因此递增,即时事件就在这个瞬间发生了。写满状态成立,iTag[1]也随之拉高即时值。从时序上来看,C1的更新(C1为4’b100)是发生在T4之后,不过那也无关紧要,因为即时值是更新在小小的时间沿之间,也是即时层 ... 然而,即时层是无法显示在时序之上。

clip_image030

图15.15 迟到的写满状态。

假设,iTag[1]不是经由即时事件触发而是事件点事件,那么iTag就会反馈迟到的写满状态。如图15.15所示,T4之际 oEn[1] 为高,C1也因此递增为 3’b100。T5之际,C1与C2的过去值均为 3’b100 与 3’b000,然后拉高 iTag[1]。由于时间点事件的关系,所以iTag[1]迟一拍被拉高 ... 读者千万别小看这样慢来一拍,它是搞乱调用的罪魁祸首。

如果核心操作在T5继续写操作的话,此刻iTag[1]的过去值为0,它会认为FIFO未满,然后不管三七二十一执行写操作,结果FIFO发生错乱随之机能崩溃。从某种程度来看,即时事件的偷时钟能力,是建立同步FIFO的关键。

fifo_savemod.v

clip_image032

图15.16 fifo储存模块的建模图。

图15.16基本上与图15.2没什么两样,不过FIFO储存模块的位宽还有深度发生改变而已。此外,图15.16的信号布局虽然有点违规低,不过这点小细节读者就不要太计较了。建模技巧毕竟不是暴力规范,用不着死守,反之随机应变才是本意。

1. module fifo_savemod

2. (

3. input CLOCK, RESET, 

4. input [1:0]iEn,

5. input [7:0]iData,

6. output [7:0]oData,

7. output [1:0]oTag

8. );

以上内容是相关的出入端声明。

9. reg [7:0] RAM [15:0]; 

10. reg [7:0]D1;

11. reg [4:0]C1,C2; // N+1

12.

以上内容是相关的内存与寄存器声明。第9行,RAM声明为8位宽还有24=16个深度。为此,第11行的写指针C1与读指针C2声明为5个位宽。

13. always @ ( posedge CLOCK or negedge RESET )

14. if( !RESET )

15. begin

16. C1 <= 5'd0;

17. end

18. else if( iEn[1] ) 

19. begin

20. RAM[ C1[3:0] ] <= iData; 

21. C1 <= C1 + 1'b1; 

22. end

23.

以上内容是fifo的写操作,第18行的 iEn[1] 每拉高一个时钟, 第20行的iData 便写入C1[3:0]指定的位置,随后第21行写指针也递增。

24. always @ ( posedge CLOCK or negedge RESET )

25. if( !RESET )

26. begin

27. D1 <= 8'd0;

28. C2 <= 5'd0;

29. end

30. else if( iEn[0] )

31. begin 

32. D1 <= RAM[ C2[3:0] ]; 

33. C2 <= C2 + 1'b1; 

34. end

35.

以上内容是fifo的读操作,第30行的 iEn[0] 每拉高一个时钟, 第32行的D1便赋予C2[3:0]指定的数据,随后第33行的读指针也递增。

36. assign oData = D1;

37. assign oTag[1] = ( C1[4]^C2[4] & C1[3:0] == C2[3:0] ); // Full

38. assign oTag[0] = ( C1 == C2 ); // Empty

39.

40. endmodule

以上内容是相关输出驱动声明,其中第37行是写满状态,第38行是读空状态。在此,读者需要注意一下 ... 第36行相较第37~38行 ,前者由寄存器D1驱动,即oData信号为时间点事件。反之,后者由组合逻辑驱动,即 oTag[1:0] 信号为即时事件。为此,该储存模块的内部状态是以即时的方式反馈出去。

tx_rx_demo.v

clip_image034

图15.17 实验十五的建模图。

实验十五是实验十三的延续 ... 实验十三之际,RX功能模块接收并且失败发送一连串的数据,因为发送方不仅来不及,而且接收成功的数据也没有地方缓冲。如今实验十五多了一只FIFO储存模块作为缓冲空间。注意,图15.17虽然是实验十五的建模图,可是却与实际的连线部署有一点出入,不过大意上都是差不多的。

RX功能模块接收一连串的数据,然后经由周边操作协调,事后再将数据缓冲至FIFO储存模块。至于核心操作会不停从FIFO储存模块哪里读取数据,然后再调用TX功能模块将数据发送出去。

1. module tx_rx_demo

2. (

3. input CLOCK, RESET, 

4. input RXD,

5. output TXD

6. ); 

以上内容是相关的出入端声明。

7. wire DoneU1;

8. wire [7:0]DataU1;

9.

10. rx_funcmod U1

11. (

12. .CLOCK( CLOCK ),

13. .RESET( RESET ),

14. .RXD( RXD ), // < top

15. .iCall( isRX ), // < sub

16. .oDone( DoneU1 ), // > U2

17. .oData( DataU1 ) // > U2

18. );

19.

以上内容是RX功能模块的实例化。第15行表示 isRX 充当使能。

20. reg isRX;

21.

22. always @ ( posedge CLOCK or negedge RESET ) // sub

23. if( !RESET ) isRX <= 1'b0;

24. else if( DoneU1 ) isRX <= 1'b0; 

25. else isRX <= 1'b1; 

26.

以上内容是周边操作,它主要重复调用RX功能模块。

27. wire [1:0]TagU2;

28. wire [7:0]DataU2;

29.

30. fifo_savemod U2

31. (

32. .CLOCK( CLOCK ),

33. .RESET( RESET ),

34. .iEn ( { DoneU1 , isRead } ), // < U1 & Core

35. .iData ( DataU1 ), // < U1

36. .oData ( DataU2 ), // > U3

37. .oTag ( TagU2 ) // > core

38. );

39.

以上内容是FIFO储存模块的实例化。第34行表示,DoneU1充当写入使能,isRead充当读出使能。

40. wire DoneU3;

41.

42. tx_funcmod U3

43. (

44. .CLOCK( CLOCK ), 

45. .RESET( RESET ),

46. .TXD( TXD ), // > top

47. .iCall( isTX ), // < core

48. .oDone( DoneU3 ), // > core

49. .iData( DataU2 ) // < U2

50. );

51.

以上内容是TX功能模块的实例化。第47行表示 isTX充当使能。第49行表示,该模块的iData直接经由DataU2驱动。

52. reg [3:0]i;

53. reg isRead;

54. reg isTX;

55.

56. always @ ( posedge CLOCK or negedge RESET ) // core

57. if( !RESET )

58. begin 

59. i <= 4'd0;

60. isRead <= 1'b0;

61. isTX<= 1'b0;

62. end

以上内容是核心操作的相关寄存器声明与复位操作。

63. else

64. case( i )

65.

66. 0:

67. if( !TagU2[0] ) begin isRead <= 1'b1; i <= i + 1'b1; end

68.

69. 1:

70. begin isRead <= 1'b0; i <= i + 1'b1; end

71.

72. 2:

73. if( DoneU3 ) begin isTX <= 1'b0; i <= 4'd0; end

74. else isTX <= 1'b1;

75.

76. endcase

77.

78. endmodule

以上内容是操作操作。步骤0用来判断FIFO是否读空,否则就拉高isRead。步骤1则拉低isRead,然后FIFO就会读出数据。步骤2则使能TX功能模块,并且将方才读出的数据发送出去。

编译完毕并下载程序。此刻,串口便可以支持一连串的数据发送与接收,为了避免部分数据凭空消失的怪事,数据流的容量必须配合FIFO的缓冲容量(深度)。此外,实验十五还有许多优化的空间,然而这些都是交由读者的功课。(注意,某些串口调试助手必须把检验位设置为标志位才能显示字符)

细节一:完整的个体模块

该实验的 fifo_savemod.v 已经是完整的个体。

目录
相关文章
|
14天前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的16QAM调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本项目基于FPGA实现了16QAM基带通信系统,包括调制、信道仿真、解调及误码率统计模块。通过Vivado2019.2仿真,设置不同SNR(如8dB、12dB),验证了软解调相较于传统16QAM系统的优越性,误码率显著降低。系统采用Verilog语言编写,详细介绍了16QAM软解调的原理及实现步骤,适用于高性能数据传输场景。
113 69
|
19天前
|
移动开发 算法 数据安全/隐私保护
基于FPGA的QPSK调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的QPSK调制解调系统,通过Vivado 2019.2进行仿真,展示了在不同信噪比(SNR=1dB, 5dB, 10dB)下的仿真效果。与普通QPSK系统相比,该系统的软解调技术显著降低了误码率。文章还详细阐述了QPSK调制的基本原理、信号采样、判决、解调及软解调的实现过程,并提供了Verilog核心程序代码。
53 26
|
5天前
|
数据采集 算法 数据安全/隐私保护
【硬件测试】基于FPGA的2FSK调制解调系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR
本文介绍了基于FPGA的2FSK调制解调系统,包含高斯信道、误码率统计模块及testbench。系统增加了ILA在线数据采集和VIO在线SNR设置模块,支持不同SNR下的硬件测试,并提供操作视频指导。理论部分涵盖频移键控(FSK)原理,包括相位连续与不连续FSK信号的特点及功率谱密度特性。Verilog代码实现了FSK调制解调的核心功能,支持在不同开发板上移植。硬件测试结果展示了不同SNR下的性能表现。
25 6
|
25天前
|
算法 异构计算
基于FPGA的4ASK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的4-ASK调制解调系统的算法仿真效果、理论基础及Verilog核心程序。仿真在Vivado2019.2环境下进行,分别测试了SNR为20dB、15dB、10dB时的性能。理论部分概述了4-ASK的工作原理,包括调制、解调过程及其数学模型。Verilog代码实现了4-ASK调制器、加性高斯白噪声(AWGN)信道模拟、解调器及误码率计算模块。
47 8
|
1月前
|
算法 物联网 异构计算
基于FPGA的4FSK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的4FSK调制解调系统的Verilog实现,包括高斯信道模块和误码率统计模块,支持不同SNR设置。系统在Vivado 2019.2上开发,展示了在不同SNR条件下的仿真结果。4FSK调制通过将输入数据转换为四个不同频率的信号来提高频带利用率和抗干扰能力,适用于无线通信和数据传输领域。文中还提供了核心Verilog代码,详细描述了调制、加噪声、解调及误码率计算的过程。
51 11
|
5月前
|
数据采集 传感器 监控
如何在LabVIEW中使用FPGA模块
如何在LabVIEW中使用FPGA模块
164 1
|
1月前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的1024QAM基带通信系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的1024QAM调制解调系统的仿真与实现。通过Vivado 2019.2进行仿真,分别在SNR=40dB和35dB下验证了算法效果,并将数据导入Matlab生成星座图。1024QAM调制将10比特映射到复数平面上的1024个星座点之一,适用于高数据传输速率的应用。系统包含数据接口、串并转换、星座映射、调制器、解调器等模块。Verilog核心程序实现了调制、加噪声信道和解调过程,并统计误码率。
45 1
|
2月前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的64QAM基带通信系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的64QAM调制解调通信系统的设计与实现,包括信号生成、调制、解调和误码率测试。系统在Vivado 2019.2中进行了仿真,通过设置不同SNR值(15、20、25)验证了系统的性能,并展示了相应的星座图。核心程序使用Verilog语言编写,加入了信道噪声模块和误码率统计功能,提升了仿真效率。
56 4
|
2月前
|
存储 算法 数据处理
基于FPGA的8PSK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本系统在原有的8PSK调制解调基础上,新增了高斯信道与误码率统计模块,验证了不同SNR条件下的8PSK性能。VIVADO2019.2仿真结果显示,在SNR分别为30dB、15dB和10dB时,系统表现出不同的误码率和星座图分布。8PSK作为一种高效的相位调制技术,广泛应用于无线通信中。FPGA凭借其高度灵活性和并行处理能力,成为实现此类复杂算法的理想平台。系统RTL结构展示了各模块间的连接与协同工作。
63 16
|
2月前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的16QAM基带通信系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本项目基于FPGA实现16QAM调制解调通信系统,使用Verilog语言编写,包括信道模块、误码率统计模块。通过设置不同SNR值(如8dB、12dB、16dB),仿真测试系统的误码性能。项目提供了完整的RTL结构图及操作视频,便于理解和操作。核心程序实现了信号的生成、调制、信道传输、解调及误码统计等功能。
56 3