#今天也是咸鱼的一天~(换口头禅了)
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核,所以就用源代码写一个。
参考是是这两份博客:
参考的资料是这个:
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 =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
赋值没有用非阻塞赋值,不过仿真的时候没有错误,数据可以正常显示。但是为了代码规范,还是用<=
比较稳妥一些。
颜色可以在右侧的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的三目运算符)