搜索
bottom↓
回复: 11

初学者 Verilog 找茬

[复制链接]

出0入0汤圆

发表于 2010-12-15 23:47:26 | 显示全部楼层 |阅读模式
先说我程序的目的:用FPGA循环输出8位数字量。用了外部晶振提供CLK,三个拨动开关R0,R1,R2,控制输出数据,数据在CLK上升沿输出。还有一个复位按钮,高电平复位。

程序一:
module DM412(CLK,rst,DAI,R0,R1,R2)
input CLK,,rst,R0,R1,R2;
output DAI;
reg[7:0] 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[7-J];//这样的为什么都不行,仿真结果每个CLK上升沿输出的数据都是是DAI=DATA[7],完全无视J的存在!
               J<=J+1'b1;
        end
endmodule




程序一:// 这是个超级废品!
module DM412(CLK,rst,DAI,R0,R1,R2)
input CLK,,rst,R0,R1,R2;
output DAI;
reg[7:0] 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[7];   
              DATA<=DATA<<1;// DATA左移一位, 问题是输完8次后数据不是会全变0了吗? 我是想要循环输出的啊,怎么办?
              end
endmodule
/*=ERROR===提示错误:DATA不能多次赋值,会产生竞争,但是我这里既要想既要给DATA赋初值,又要变化它 怎么办?======*/

程序二://为了改变程序二的错误 写了这个,还是失败的说!
module DM412(CLK,rst,DAI,R0,R1,R2)
input CLK,,rst,R0,R1,R2;
output DAI,;
reg[7:0] 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[7:0]<=({DATA[0],DATA[7:1]})  //  这样可以让数据循环??? 我听说的。
                 DAI<=DATA0[7];   
                            end
endmodule



/*==============我只是想写一个如此简单的程序 为什么都不行呢? 高手来帮忙了。
  R0,R1,R2本来可以组成8种状态,这样就可以选择8种DATA[7:0],可以用case语句来给========================*/
always@(R1 or R2 or R3)//在同一个程序中的不同always中,可以分别有电平和上升沿的触发吗?我知道在同一个always中不能同时有。
if(rst)
  begin  DATA<=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
/*==============上面这段可以吗?============================*/

/*======数据的循环给到底要怎么给,坐等高手来解答,多照顾下新手哈===============*/

/*===下面这个是好的哈, 时序图,将60MHZ的外部晶振OSC进行3分频得到20MHZ的时钟clkD,由clkD再整出CKI来,CKI的要求是48个上升沿后保持三个周期的高电平,然后循环。   48位数据就是在CKI上升沿时给,给完48位后在三个周期的高电平阶段,数据给8个脉冲。然后循环。
现在我主要就是差的给数据!! =============================*/

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[3:0]     count1,count2;
reg[8:0]     count3;
reg          clkA,clkB,clkC;
wire         clk_re;
wire           CKI,clkD,clkE;

reg [6:0] J;

reg [2:0] T;
reg [9:0] K;


reg DAI,STI;

/*==========================================*/
wire [47:0] DATA;
reg [47:0] 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 begin  clkC<=1'b1; end
   
endmudule   

结果:


(原文件名:1.GIF)


(原文件名:2.GIF)


中间有9个脉冲,因为是三分频 只能是3的倍数,其实要求的DAI是8个脉冲能启动自动锁存的功能,但9个应该也可以吧 , 我只能做到这份上 。 呜呜呜~~



总得来说,我就是不知道怎么循环给数据,您要是能看完整个帖子,我就知足了~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2010-12-16 01:47:53 | 显示全部楼层
呃,晚上太晚我头有点疼。试着评论一下。

        begin DAI<=DATA[7-J];//这样的为什么都不行,仿真结果每个CLK上升沿输出的数据都是是DAI=DATA[7],完全无视J的存在!
               J<=J+1'b1;
        end  

我没这么写过。如果我要你的功能,我会这样写一段代码:

(前面都略)

reg data_s;

always@(*)
begin
datas = 1'bx;
case(j)
0:datas = DATA[7];
1:datas = DATA[6];
2:datas = DATA[5];
3:datas = DATA[4];
4:datas = DATA[3];
5:datas = DATA[2];
6:datas = DATA[1];
7:datas = DATA[0];
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

出0入0汤圆

发表于 2010-12-16 01:50:45 | 显示全部楼层
/*=ERROR===提示错误:DATA不能多次赋值,会产生竞争,但是我这里既要想既要给DATA赋初值,又要变化它 怎么办?======*/

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

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

这两条绝对可以做到。

出0入0汤圆

发表于 2010-12-16 01:51:12 | 显示全部楼层
DATA[7:0]<=({DATA[0],DATA[7:1]})  //  这样可以让数据循环??? 我听说的。

是的,这样就可以。

出0入0汤圆

发表于 2010-12-16 01:55:31 | 显示全部楼层
R0,R1,R2本来可以组成8种状态,这样就可以选择8种DATA[7:0],可以用case语句来给========================*/
always@(R1 or R2 or R3)//在同一个程序中的不同always中,可以分别有电平和上升沿的触发吗?我知道在同一个always中不能同时有。
if(rst)
  begin  DATA<=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。

出0入0汤圆

发表于 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 )      

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

出0入0汤圆

发表于 2010-12-16 09:04:12 | 显示全部楼层
学习!
ngzhang 兽哥
和热心!

出0入0汤圆

发表于 2010-12-16 11:41:53 | 显示全部楼层
mark...

出0入0汤圆

发表于 2010-12-16 13:18:35 | 显示全部楼层
初学者

出0入0汤圆

 楼主| 发表于 2010-12-16 13:45:17 | 显示全部楼层


非常感谢【 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[7];
、、、、、、
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[7-J];//这样的为什么都不行,仿真结果每个CLK上升沿输出的数据都是是DAI=DATA[7],完全无视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[7];   
              DATA<=DATA<<1;//
              end
这样的话就是会出现给DATA赋值两次的错误。但是我又想用这功能,该怎么办呢?
而且多设一个变量也是不行的,例如:
always@(posedge CLK)
         begin
               DAI<=datas[7];   
              datas<=DATA<<1;//
              end

这个的结果是datas等于只是DATA移动一位,不会循环移动,如果DATA=8'b1111 0000,那么datas就恒定等于8‘b1110 0000.


最后:是时序问题,我很多时钟都是直接变换过来的,这样确实会有很多问题,像【ngzhang 兽哥】说的那样。 我正在学习【ngzhang 兽哥】说的DCM,目前还没有眉目,哈哈,还要多学习。

晒晒结果;

(原文件名:1.GIF)

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

出0入0汤圆

发表于 2010-12-16 14:12:35 | 显示全部楼层
MARK,学习

出0入0汤圆

发表于 2010-12-16 15:03:16 | 显示全部楼层
回复【9楼】liu116feng  

非常感谢【 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之类的都不要用。

最后就是,写的是代码,心中浮现的是硬件结构。如果写完了代码,大概会生成一个什么东西心理还没谱,那计算姬同样心理也没谱,最后能不能满足需要那就也没谱了。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-24 15:19

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表