liu116feng 发表于 2010-12-15 23:47:26

初学者 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
总得来说,我就是不知道怎么循环给数据,您要是能看完整个帖子,我就知足了~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ngzhang 发表于 2010-12-16 01:47:53

呃,晚上太晚我头有点疼。试着评论一下。

      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

ngzhang 发表于 2010-12-16 01:50:45

/*=ERROR===提示错误:DATA不能多次赋值,会产生竞争,但是我这里既要想既要给DATA赋初值,又要变化它 怎么办?======*/

关于这个问题。
我的建议是以下两条:

1,在一个always块里,只对一个变量进行赋值操作。//为了代码的清晰性。
2,对一个变量来说,只在一个always块里对其进行赋值操作。//这样做就绝不会出你的error。

这两条绝对可以做到。

ngzhang 发表于 2010-12-16 01:51:12

DATA<=({DATA,DATA})//这样可以让数据循环??? 我听说的。

是的,这样就可以。

ngzhang 发表于 2010-12-16 01:55:31

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。

ngzhang 发表于 2010-12-16 02:01:27

下面来说说最后的那段程序。
目的不是改错,就随便说说写法的问题。
问题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 )      

不要在同一个设计中同时使用同步复位和异步复位。除非你知道自己在干什么或者有特殊需要。否则出问题会令你很困惑。

zzjjhh250 发表于 2010-12-16 09:04:12

学习!
ngzhang 兽哥
和热心!

yl604922959 发表于 2010-12-16 11:41:53

mark...

cooleaf 发表于 2010-12-16 13:18:35

初学者

liu116feng 发表于 2010-12-16 13:45:17

./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)

*/========================

antonine 发表于 2010-12-16 14:12:35

MARK,学习

ngzhang 发表于 2010-12-16 15:03:16

回复【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]
查看完整版本: 初学者 Verilog 找茬