【FPGA】Robei EDA的使用(5)——DDS的发生

#今天也是咸鱼的一天~(换口头禅了)

DDS全称直接数字频率合成(Direct Digital Synthesis),系统结构可分为这几个部分,其中相位控制字可调整输出正弦波的相位,频率控制字可以调整输出正弦波的频率。
在这里插入图片描述
BUT,我在这里必须吐槽一下,Robei比赛的骚操作(别打我),比赛不允许我们使用除了RISC-V 以外的IP核,如果这个在后面真的不做任何改动的话,这就意味着,我没法使用像DDS,PLL,SPI,ROM,RAM,EEPROM,FIFO的IP核(菜鸡流泪),我可能得全部自己手写,手写各种通信协议和常用IP核。因此,这次介绍的是这样一个常用IP核,DDS。(vivado是有dds的IP,quartus好像是没有的)
在这里插入图片描述
DDS是一个在很多地方都有很大的用处的硬件,能够产生多种波形,包括现在的信号源,大多也是用FPGA做的,因为Robei没法使用这些IP核,所以就用源代码写一个。
参考是是这两份博客:

  1. DDS的FPGA实现
  2. DDS原理及FPGA实现
    ————————————————毫无感情的分割线————————————————

方波发生

参考的资料是这个:
在这里插入图片描述
Robei 模块图:
在这里插入图片描述
绿色的模块是一个16进制的计数器,另两个蓝色的模块,是对计数器的数据进行处理,实现方波的过程。

计数器

在这里插入图片描述
Code:

// let's create a 16 bits free-running binary counter
always @(posedge clk or negedge rst)
if(!rst)
	cnt  <= 16'h0;
else if(cnt == 16'hff)
	cnt <= 16'h0;
else
 cnt <= cnt + 16'h1;
// and use it to generate the DAC signal output

取位数

在这里插入图片描述

assign cnt_tap = cnt[7];     // we take one bit out of the counter (here bit 7 = the 8th bit)

累乘

在这里插入图片描述

assign DAC_data = {10{cnt_tap}};   // and we duplicate it 10 times to create the 10-bits DAC value 

三角波

在这里插入图片描述
code:

assign tri_data = cnt_tap [10] ?  ~cnt_tap [9:0] : cnt_tap [9:0];

锯齿波

在这里插入图片描述

assign saw_data = cnt_tap[9:0];

testbech

在这里插入图片描述

initial begin 
clk = 0;
rst = 0;
#5 rst = 1;
#5 clk = 1;
#150000 rst = 0;
#5  $finish;
end
always begin 
#5 clk =~clk;
end

信号波形

在这里插入图片描述

——————菜鸡冷漠的分割线————————

前几个都是很简单的代码,利用计数器就可以实现,这是因为像方波、三角波、锯齿波这类波形,都有很“线性”的关系。方波是边沿陡峭,其它平稳;锯齿波是一个线性上升,再边沿陡峭;三角波是线性上升,线性下降。这些关系都比较容易模拟,但是对于正弦波这样的信号波形,它的函数是sin,这样子在数字量输出上,就比较惨了~

在Quartus的编程里,有Rom可以存储波形数据,直接输出就行,非常方便;更是直接有现成的IP Core 可以调用DDS。但是Robei既没有ROM,也没有IP,所以只能手写数据来输出正弦波。

模块配置

分频counter

在这里插入图片描述
Code:

reg [10:0] num;
always@(posedge clk or negedge rst)
begin
	if(!rst)
	begin
		num <= 11'd0;
		clk_1 <= 1'b0;
	end
	else if(num>=1280)  //这个是对系统时钟分频,根据想得到正弦波的频率,算这里取多少
	begin
		clk_1<=~clk_1;
		num <=11'd0;
	end
	else
		num <=num+11'b1;
end

这个计数器就是一个简易的分频器,当然,如果有条件,直接用pll 多简单。

波形数据

在这里插入图片描述
Code:

reg [7:0] addr ;
always@(posedge clk_1 or negedge rst)
begin
	if(!rst)
		addr <= 8'd0;
	else 
	begin
	addr<=addr+1;
	case(addr)
	8'd0:out=8'h80;
	8'd1:out=8'h83;		
	8'd2:out=8'h86;
	8'd3:out=8'h89;
	8'd4:out=8'h8d;
	8'd5:out=8'h90;
	8'd6:out=8'h93;
	8'd7:out=8'h96;
	8'd8:out=8'h99;
	8'd9:out=8'h9c;
	8'd10:out=8'h9f;
	8'd11:out=8'ha2;
	8'd12:out=8'ha5;
	8'd13:out=8'ha8;
	8'd14:out=8'hab;
	8'd15:out=8'hae;
	8'd16:out=8'hb1;
	8'd17:out=8'hb4;
	8'd18:out=8'hb7;
	8'd19:out=8'hba;
	8'd20:out=8'hbc;
	8'd21:out=8'hbf;
	8'd22:out=8'hc2;
	8'd23:out=8'hc5;
	8'd24:out=8'hc7;
	8'd25:out=8'hca;
	8'd26:out=8'hcc;
	8'd27:out=8'hcf;
	8'd28:out=8'hd1;
	8'd29:out=8'hd4;
	8'd30:out=8'hd6;
	8'd31:out=8'hd8;
	8'd32:out=8'hda;
	8'd33:out=8'hdd;
	8'd34:out=8'hdf;
	8'd35:out=8'he1;
	8'd36:out=8'he3;
	8'd37:out=8'he5;
	8'd38:out=8'he7;
	8'd39:out=8'he9;
	8'd40:out=8'hea;
	8'd41:out=8'hec;
	8'd42:out=8'hee;
	8'd43:out=8'hef;
	8'd44:out=8'hf1;
	8'd45:out=8'hf2;
	8'd46:out=8'hf4;
	8'd47:out=8'hf5;
	8'd48:out=8'hf6;
	8'd49:out=8'hf7;
	8'd50:out=8'hf8;
	8'd51:out=8'hf9;
	8'd52:out=8'hfa;
	8'd53:out=8'hfb;
	8'd54:out=8'hfc;
	8'd55:out=8'hfd;
	8'd56:out=8'hfd;
	8'd57:out=8'hfe;
	8'd58:out=8'hff;
	8'd59:out=8'hff;
	8'd60:out=8'hff;
	8'd61:out=8'hff;
	8'd62:out=8'hff;
	8'd63:out=8'hff;
	8'd64:out=8'hff;
	8'd65:out=8'hff;
	8'd66:out=8'hff;
	8'd67:out=8'hff;
	8'd68:out=8'hff;
	8'd69:out=8'hff;
	8'd70:out=8'hfe;
	8'd71:out=8'hfd;
	8'd72:out=8'hfd;
	8'd73:out=8'hfc;
	8'd74:out=8'hfb;
	8'd75:out=8'hfa;
	8'd76:out=8'hf9;
	8'd77:out=8'hf8;
	8'd78:out=8'hf7;
	8'd79:out=8'hf6;
	8'd80:out=8'hf5;
	8'd81:out=8'hf4;
	8'd82:out=8'hf2;
	8'd83:out=8'hf1;
	8'd84:out=8'hef;
	8'd85:out=8'hee;
	8'd86:out=8'hec;
	8'd87:out=8'hea;
	8'd88:out=8'he9;
	8'd89:out=8'he7;
	8'd90:out=8'he5;
	8'd91:out=8'he3;
	8'd92:out=8'he1;
	8'd93:out=8'hde;
	8'd94:out=8'hdd;
	8'd95:out=8'hda;
	8'd96:out=8'hd8;
	8'd97:out=8'hd6;
	8'd98:out=8'hd4;
	8'd99:out=8'hd1;
	8'd100:out=8'hcf;
	8'd101:out=8'hcc;
	8'd102:out=8'hca;
	8'd103:out=8'hc7;
	8'd104:out=8'hc5;
	8'd105:out=8'hc2;
	8'd106:out=8'hbf;
	8'd107:out=8'hbc;
	8'd108:out=8'hba;
	8'd109:out=8'hb7;
	8'd110:out=8'hb4;
	8'd111:out=8'hb1;
	8'd112:out=8'hae;
	8'd113:out=8'hab;
	8'd114:out=8'ha8;
	8'd115:out=8'ha5;
	8'd116:out=8'ha2;
	8'd117:out=8'h9f;
	8'd118:out=8'h9c;
	8'd119:out=8'h99;
	8'd120:out=8'h96;
	8'd121:out=8'h93;
	8'd122:out=8'h90;
	8'd123:out=8'h8d;
	8'd124:out=8'h89;
	8'd125:out=8'h86;
	8'd126:out=8'h83;
	8'd127:out=8'h80;
	8'd128:out=8'h80;
	8'd129:out=8'h7c;
	8'd130:out=8'h79;
	8'd131:out=8'h76;
	8'd132:out=8'h72;
	8'd133:out=8'h6f;
	8'd134:out=8'h6c;
	8'd135:out=8'h69;
	8'd136:out=8'h66;
	8'd137:out=8'h63;
	8'd138:out=8'h60;
	8'd139:out=8'h5d;
	8'd140:out=8'h5a;
	8'd141:out=8'h57;
	8'd142:out=8'h55;
	8'd143:out=8'h51;
	8'd144:out=8'h4e;
	8'd145:out=8'h4c;
	8'd146:out=8'h48;
	8'd147:out=8'h45;
	8'd148:out=8'h43;
	8'd149:out=8'h40;
	8'd150:out=8'h3d;
	8'd151:out=8'h3a;
	8'd152:out=8'h38;
	8'd153:out=8'h35;
	8'd154:out=8'h33;
	8'd155:out=8'h30;
	8'd156:out=8'h2e;
	8'd157:out=8'h2b;
	8'd158:out=8'h29;
	8'd159:out=8'h27;
	8'd160:out=8'h25;
	8'd161:out=8'h22;
	8'd162:out=8'h20;
	8'd163:out=8'h1e;
	8'd164:out=8'h1c;
	8'd165:out=8'h1a;
	8'd166:out=8'h18;
	8'd167:out=8'h16 ;
	8'd168:out=8'h15;
	8'd169:out=8'h13;
	8'd170:out=8'h11;
	8'd171:out=8'h10;
	8'd172:out=8'h0e;
	8'd173:out=8'h0d;
	8'd174:out=8'h0b;
	8'd175:out=8'h0a;
	8'd176:out=8'h09;
	8'd177:out=8'h08;
	8'd178:out=8'h07;
	8'd179:out=8'h06;
	8'd180:out=8'h05;
	8'd181:out=8'h04;
	8'd182:out=8'h03;
	8'd183:out=8'h02;
	8'd184:out=8'h02;
	8'd185:out=8'h01;
	8'd186:out=8'h00;
	8'd187:out=8'h00;
	8'd188:out=8'h00;
	8'd189:out=8'h00;
	8'd190:out=8'h00;
	8'd191:out=8'h00;
	8'd192:out=8'h00;
	8'd193:out=8'h00;
	8'd194:out=8'h00;
	8'd195:out=8'h00;
	8'd196:out=8'h00;
	8'd197:out=8'h00;
	8'd198:out=8'h01;
	8'd199:out=8'h02 ;
	8'd200:out=8'h02;
	8'd201:out=8'h03;
	8'd202:out=8'h04;
	8'd203:out=8'h05;
	8'd204:out=8'h06;
	8'd205:out=8'h07;
	8'd206:out=8'h08;
	8'd207:out=8'h09;
	8'd208:out=8'h0a;
	8'd209:out=8'h0b;
	8'd210:out=8'h0d;
	8'd211:out=8'h0e;
	8'd212:out=8'h10;
	8'd213:out=8'h11;
	8'd214:out=8'h13;
	8'd215:out=8'h15 ;
	8'd216:out=8'h16;
	8'd217:out=8'h18;
	8'd218:out=8'h1a;
	8'd219:out=8'h1c;
	8'd220:out=8'h1e;
	8'd221:out=8'h20;
	8'd222:out=8'h22;
	8'd223:out=8'h25;
	8'd224:out=8'h27;
	8'd225:out=8'h29;
	8'd226:out=8'h2b;
	8'd227:out=8'h2e;
	8'd228:out=8'h30;
	8'd229:out=8'h33;
	8'd230:out=8'h35;
	8'd231:out=8'h38;
	8'd232:out=8'h3a;
	8'd233:out=8'h3d;
	8'd234:out=8'h40;
	8'd235:out=8'h43;
	8'd236:out=8'h45;
	8'd237:out=8'h48;
	8'd238:out=8'h4c;
	8'd239:out=8'h4e;
	8'd240:out=8'h51;
	8'd241:out=8'h55;
	8'd242:out=8'h57;
	8'd243:out=8'h5a;
	8'd244:out=8'h5d;
	8'd245:out=8'h60;
	8'd246:out=8'h63;
	8'd247:out=8'h66 ;
	8'd248:out=8'h69;
	8'd249:out=8'h6c;
	8'd250:out=8'h6f;
	8'd251:out=8'h72;
	8'd252:out=8'h76;
	8'd253:out=8'h79;
	8'd254:out=8'h7c;
	8'd255:out=8'h80;
	endcase
	end
end

这里的out赋值没有用非阻塞赋值,不过仿真的时候没有错误,数据可以正常显示。但是为了代码规范,还是用<=比较稳妥一些。

testbech

在这里插入图片描述
颜色可以在右侧的Color那里修改,不改也没有影响,这里看着好看而已。
code:

initial begin 
clk = 0;
rst = 0;
#5 rst = 1;
#5 clk = 1;
#150000 rst = 0;
#5  $finish;
end

always begin 
#1 clk =~clk;
end

这里我分频分的有点多,因为实际的FPGA的时钟频率都挺高的,不分多点不好看。但是仿真的话,这个数据就有点丑,将就将就。

波形显示

在这里插入图片描述
前面看这里都是红色,就是因为计数还没够,所以没有输出。
在这里插入图片描述
现在的效果就很明显了,不过,因为这个实际波形要用DAC 输出来看,Robei好像没有像Modesim那样可以合并数据看波形的功能,所以至此就是仿真成功了。

————————————正文结束的分割线————————————

FPGA的本质是什么?
复读机
逻辑单元构成的数字电路,所以在FPGA里,利用各种基础的逻辑运算来优化Verilog代码,在实际中,可以很大程度提高计算/处理能力。(当然这个得优化到宏观,才能从一定程度体现出来,就我现在这个水平的代码,优不优化没区别)
与,或,非,异或,?:,这些,可以在一些 tip 上用,来达到巧妙的化简代码。(比如上次PWM的三目运算符)

发布了22 篇原创文章 · 获赞 28 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Ninquelote/article/details/105668155
今日推荐