初学者 Verilog 找茬
先说我程序的目的:用FPGA循环输出8位数字量。用了外部晶振提供CLK,三个拨动开关R0,R1,R2,控制输出数据,数据在CLK上升沿输出。还有一个复位按钮,高电平复位。程序一:
module DM412(CLK,rst,DAI,R0,R1,R2)
input CLK,,rst,R0,R1,R2;
output DAI;
reg DAI,J;
reg DATA;
lways @ (posedge CLK or posedge rst)
if(rst)
begin DATA<=1'b0;end
else
begin
DATA = (R0)?8'b00000000// R0,R1,R2选择8位输出数据
:((R1)?8'b10101010
:((R2)?8'b11111111));
end
always @ (posedge CLK or posedge rst)
if(rst)
begin DAI<=1'b0;
J<=1'b0;
end
else
begin DAI<=DATA;//./emotion/em019.gif这样的为什么都不行,仿真结果每个CLK上升沿输出的数据都是是DAI=DATA,完全无视J的存在!
J<=J+1'b1;
end
endmodule
程序一:// 这是个超级废品!
module DM412(CLK,rst,DAI,R0,R1,R2)
input CLK,,rst,R0,R1,R2;
output DAI;
reg DAI;
reg DATA;
lways @ (posedge CLK or posedge rst)
if(rst)
begin DATA<=1'b0;end
else
begin
DATA = (R0)?8'b00000000// R0,R1,R2
:((R1)?8'b10101010
:((R2)?8'b11111111));
end
always @ (posedge CLK or posedge rst)
if(rst)
begin DAI<=1'b0;end
else
begin DAI<=DATA;
DATA<=DATA<<1;// ./emotion/em019.gifDATA左移一位, 问题是输完8次后数据不是会全变0了吗? 我是想要循环输出的啊,怎么办?
end
endmodule
/*=ERROR===提示错误:DATA不能多次赋值,会产生竞争,但是我这里既要想既要给DATA赋初值,又要变化它 怎么办?./emotion/em019.gif======*/
程序二://为了改变程序二的错误 写了这个,还是失败的说!
module DM412(CLK,rst,DAI,R0,R1,R2)
input CLK,,rst,R0,R1,R2;
output DAI,;
reg DAI,DATA0;
wire DATA;
lways @ (posedge CLK or posedge rst)
if(rst)
begin DATA<=1'b0;end
else
begin
DATA = (R0)?8'b00000000// R0,R1,R2
:((R1)?8'b10101010
:((R2)?8'b11111111));
end
always @ (posedge CLK or posedge rst)
if(rst)
begin DAI<=1'b0;end
else
begin
DATA<=({DATA,DATA})//这样可以让数据循环??? 我听说的。./emotion/em019.gif
DAI<=DATA0;
end
endmodule
/*======./emotion/em019.gif./emotion/em019.gif========我只是想写一个如此简单的程序 为什么都不行呢? 高手来帮忙了。
R0,R1,R2本来可以组成8种状态,这样就可以选择8种DATA,可以用case语句来给========================*/
always@(R1 or R2 or R3)//在同一个程序中的不同always中,可以分别有电平和上升沿的触发吗?我知道在同一个always中不能同时有。
if(rst)
beginDATA<=1'b0;end
else
begin
case({R2,R1,R0})
3'b000:begin DATA<=8'b00000000; end
3'b001:begin DATA<=8'b00000000; end
3'b010:begin DATA<=8'b00000000; end
3'b011:begin DATA<=8'b00000000; end
3'b100:begin DATA<=8'b00000000; end
3'b101:begin DATA<=8'b00000000; end
3'b110:begin DATA<=8'b00000000; end
default:begin DATA<=8'b00000000; end
endcase
end
/*==============上面这段可以吗?============================*/
/*=====./emotion/em019.gif./emotion/em019.gif=数据的循环给到底要怎么给,坐等高手来解答,多照顾下新手哈=./emotion/em019.gif==============*/
/*===下面这个是好的哈, 时序图,将60MHZ的外部晶振OSC进行3分频得到20MHZ的时钟clkD,由clkD再整出CKI来,CKI的要求是48个上升沿后保持三个周期的高电平,然后循环。 48位数据就是在CKI上升沿时给,给完48位后在三个周期的高电平阶段,数据给8个脉冲。然后循环。
现在我主要就是差的给数据!!./emotion/em035.gif =============================*/
module DM(OSC,rest,STI_I,DAI_I,SDO_I,POL_I,CKI,STI,DAI,SDO,POL,clkD,clkE,R0,R1,R2); //clk这些放这里只是为了仿真时看,***_I的都还没有用到。
input OSC,STI_I,rest,DAI_I,SDO_I,POL_I,R0,R1,R2;
output CKI,DAI,SDO,POL,STI,clkD,clkE;
reg count1,count2;
reg count3;
reg clkA,clkB,clkC;
wire clk_re;
wire CKI,clkD,clkE;
reg J;
reg T;
reg K;
reg DAI,STI;
/*==========================================*/
wire DATA;
reg DATA1;
reg CKI_RE;
wire R0,R1,R2;
assign DATA=48'HF0F;
/*================================================*/
parameter N = 3; /*N分频*/
assign clk_re = ~OSC;
/*=================================CKI=======================*/
assign clkE = clkC & OSC & CKI; //======8 MAICHONG===
assign clkD = clkA | clkB; // =========20 MHZ=====
assign CKI = clkD | clkC;
always @(posedge OSC)
if( rest)
begin
count1 <= 1'b0;
clkA <= 1'b0;
end
else
if(count1 < (N - 1))
begin
count1 <= count1 + 1'b1; /*这里是阻塞赋值是先执行了下面的IF判断,最后才赋的值。最初看这程序时没注意,想了好半天*/
if(count1 == (N - 1)/2)
begin
clkA <= ~clkA;
end
end
else
begin
clkA <= ~clkA;
count1 <= 1'b0;
end
always @ (posedge clk_re)
if( rest)
begin
count2 <= 1'b0;
clkB <= 1'b0;
end
else
if(count2 < (N - 1))
begin
count2 <= count2 + 1'b1;
if(count2 == (N - 1)/2)
begin
clkB <= ~clkB;
end
end
else
begin
clkB <= ~clkB;
count2 <= 1'b0;
end
/*========================================================*/
always @(posedge clkD or posedge rest )
if( rest)
begin
count3 <= 1'b0;
end
else
if(count3<50)
begin
count3<=count3+1'b1;
end
else begin
count3<=1'b0;
end
always @(posedge clkD)
if( rest)
begin
clkC<= 1'b0;
end
else if(count3<47)
begin clkC<=1'b0;end
else if(count3==50)
begin clkC<=1'b0;end
else beginclkC<=1'b1; end
endmudule
结果:
http://cache.amobbs.com/bbs_upload782111/files_35/ourdev_604874T9CDXU.GIF
(原文件名:1.GIF)
http://cache.amobbs.com/bbs_upload782111/files_35/ourdev_604875JARZMD.GIF
(原文件名:2.GIF)
中间有9个脉冲,因为是三分频 只能是3的倍数,其实要求的DAI是8个脉冲能启动自动锁存的功能,但9个应该也可以吧 , 我只能做到这份上 。 呜呜呜~~
./emotion/em183.gif
总得来说,我就是不知道怎么循环给数据,您要是能看完整个帖子,我就知足了~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 呃,晚上太晚我头有点疼。试着评论一下。
begin DAI<=DATA;//这样的为什么都不行,仿真结果每个CLK上升沿输出的数据都是是DAI=DATA,完全无视J的存在!
J<=J+1'b1;
end
我没这么写过。如果我要你的功能,我会这样写一段代码:
(前面都略)
reg data_s;
always@(*)
begin
datas = 1'bx;
case(j)
0:datas = DATA;
1:datas = DATA;
2:datas = DATA;
3:datas = DATA;
4:datas = DATA;
5:datas = DATA;
6:datas = DATA;
7:datas = DATA;
endcase
end
always @ (posedge CLK or posedge rst)
if(rst)
begin DAI<=1'b0;
end
else
begin DAI<=datas;
end
always @ (posedge CLK or posedge rst)
if(rst)
J<=1'b0;
end
else
begin
J<=J+1'b1;
end /*=ERROR===提示错误:DATA不能多次赋值,会产生竞争,但是我这里既要想既要给DATA赋初值,又要变化它 怎么办?======*/
关于这个问题。
我的建议是以下两条:
1,在一个always块里,只对一个变量进行赋值操作。//为了代码的清晰性。
2,对一个变量来说,只在一个always块里对其进行赋值操作。//这样做就绝不会出你的error。
这两条绝对可以做到。 DATA<=({DATA,DATA})//这样可以让数据循环??? 我听说的。
是的,这样就可以。 R0,R1,R2本来可以组成8种状态,这样就可以选择8种DATA,可以用case语句来给========================*/
always@(R1 or R2 or R3)//在同一个程序中的不同always中,可以分别有电平和上升沿的触发吗?我知道在同一个always中不能同时有。
if(rst)
beginDATA<=1'b0;end
else
begin
case({R2,R1,R0})
3'b000:begin DATA<=8'b00000000; end
3'b001:begin DATA<=8'b00000000; end
3'b010:begin DATA<=8'b00000000; end
3'b011:begin DATA<=8'b00000000; end
3'b100:begin DATA<=8'b00000000; end
3'b101:begin DATA<=8'b00000000; end
3'b110:begin DATA<=8'b00000000; end
default:begin DATA<=8'b00000000; end
endcase
end
/*==============上面这段可以吗?============================*/
你的问题在于没搞清楚组合逻辑和时序逻辑的区别。
这一段实际上你想得到的是一个ROM,所以只能写下面这些:
always@(*)//星号表示对块内所有电平变化均敏感。一定要用这个,防止综合出锁存器。
//组合逻辑没有复位
begin
DATA = 8'bx;//形式上写一句。防止出现锁存器。其实不用写就是习惯。
case({R2,R1,R0})
3'b000:begin DATA=8'b00000000; end //必须用阻塞赋值。
3'b001:begin DATA=8'b00000000; end
3'b010:begin DATA=8'b00000000; end
3'b011:begin DATA=8'b00000000; end
3'b100:begin DATA=8'b00000000; end
3'b101:begin DATA=8'b00000000; end
3'b110:begin DATA=8'b00000000; end
default:begin DATA=8'b00000000; end
endcase
end
这样的话你填入8种不同的值以后,这个模块就是3bit地址输入,8bit数据输出的ROM,容量为64bit。 下面来说说最后的那段程序。
目的不是改错,就随便说说写法的问题。
问题1。
assign clkE = clkC & OSC & CKI; //======8 MAICHONG===
assign clkD = clkA | clkB; // =========20 MHZ=====
assign CKI = clkD | clkC;
要生成时钟请使用FPGA内部的DCM或者PLL之类的时钟资源,不要自己这样搞。小设计可能还可以,大点的设计你会面临无穷多的时序问题。
问题2.
assign clk_re = ~OSC;
在fPGA设计中,不要这么简单的搞出一个反向时钟来用。请使用DCM生成反向时钟。后果同上。
问题3.
always @(posedge OSC)
always @(posedge clkD or posedge rest )
不要在同一个设计中同时使用同步复位和异步复位。除非你知道自己在干什么或者有特殊需要。否则出问题会令你很困惑。 学习!
ngzhang 兽哥
和热心! mark... 初学者 ./emotion/em183.gif
非常感谢【 ngzhang 兽哥 】!!!
原谅我这么晚才回复,我早上上班就看到了,上午一直在用【ngzhang 兽哥】教的方法编程。
现在终于能达到要求了~·再次谢谢【ngzhang 兽哥】。
下面顺便说点我刚学Verilog的感触:
首先:是觉得Verilog不像C那样精炼(大概是我还不会用的缘故吧),C里面指针的功能用起来很方便,但是Verilog我还没找到简洁的方法。像上面给数据的方法:
一:先用case({R2,R1,R0})
3'b000:begin DATA=8'b00000000; end
、、、、、、
endcase
给DATA数据。
二:把数据的每一位给datas。
always@(*)
begin
datas = 1'bx;
case(j)
0:datas = DATA;
、、、、、、
endcase
end
三:按时序输出数据。
always @ (posedge CLK or posedge rst)
if(rst)
begin DAI<=1'b0;
end
else
begin DAI<=datas;
end
/*===========================================
而我一直的想法是省去第二步(因为要给的一组数据就有很多位,像我现在用的是要给48位,这样第二步case里面就要有48条语句,写着烦),我一直是想这样写的
begin DAI<=DATA;//这样的为什么都不行,仿真结果每个CLK上升沿输出的数据都是是DAI=DATA,完全无视J的存在!
J<=J+1'b1;
end
但是结果貌似不行,也可能我还有没写对的地方吧。
其次:时序逻辑和组合逻辑的模块划分,这个可能是Verilog的中心思想,但是我刚学着用老是会用混,有时候一个变量我既想在组合逻辑里给他一个初始值的给定,在时序模块里又想让他做些变化,这样就不好用了。还有就是不能赋值两次。
比如
一:先给数据
always@(*)
case({R2,R1,R0})
3'b000:begin DATA=8'b00000000; end
、、、、、、
endcase
二:输出数据
always@(posedge CLK)
begin
DAI<=DATA;
DATA<=DATA<<1;//
end
这样的话就是会出现给DATA赋值两次的错误。但是我又想用这功能,该怎么办呢?
而且多设一个变量也是不行的,例如:
always@(posedge CLK)
begin
DAI<=datas;
datas<=DATA<<1;//
end
这个的结果是datas等于只是DATA移动一位,不会循环移动,如果DATA=8'b1111 0000,那么datas就恒定等于8‘b1110 0000.
最后:是时序问题,我很多时钟都是直接变换过来的,这样确实会有很多问题,像【ngzhang 兽哥】说的那样。 我正在学习【ngzhang 兽哥】说的DCM,目前还没有眉目,哈哈,还要多学习。
晒晒结果;
http://cache.amobbs.com/bbs_upload782111/files_35/ourdev_604944AZ8FVL.GIF
(原文件名:1.GIF)
*/======================== MARK,学习 回复【9楼】liu116feng
./emotion/em183.gif
非常感谢【 ngzhang 兽哥 】!!!
原谅我这么晚才回复,我早上上班就看到了,上午一直在用【ngzhang 兽哥】教的方法编程。
现在终于能达到要求了~·再次谢谢【ngzhang 兽哥】。
下面顺便说点我刚学verilog的感触:
首先:是觉得verilog不像c那样精炼(大概是我还不会用的缘故吧),c里面指针的功能用起来很方便,但是verilog我还没找到简洁的方法。像上面给数据的方法:
一:先用case({r2,r1,r0})
3'b000:begin data=8'b00000000; end
、、、、、、
endcase
给data数据。
二:把数据的每一位给datas。
always@(*)
begin
datas = 1'bx;
ca......
-----------------------------------------------------------------------
你像下面这样理解会有好处:
C会被编译器翻译成机器码来执行。还是一个代码到代码的转换过程。
但verilgo是完全不同的。是由综合器使之从代码转换成电路的过程。这个跨度非常的大,如果两者相比较的话,这个转换的能力相当于20年前编译器的水平。
所以说写verilog,不要追求任何代码写作上的技巧。写的越简单越好。保证自己的代码格式是大家都在写的格式,不要标新立异。同时也不要使用特定综合器才能支持的写法。简单的说,就是always块,两种复位方式,if 和else,case,再加上parameter等几个句子基本上就干什么都够了。什么task之类的都不要用。
最后就是,写的是代码,心中浮现的是硬件结构。如果写完了代码,大概会生成一个什么东西心理还没谱,那计算姬同样心理也没谱,最后能不能满足需要那就也没谱了。
页:
[1]