sme 发表于 2013-11-15 17:14:40

高手看看,能不能确定是FPGA板的问题?有代码有波形

本帖最后由 sme 于 2013-11-16 12:04 编辑

买了一块FPGA板,以前在spartan3/spartan6上好好的项目,弄到这块Artix-7上,出问题了。用片内逻辑分析仪抓了半天信号,发现是RAM问题,将电路反复复位(由上位机控制,在电路内部产生复位信号)、运行,RAM中的数据会变。
(RAM当程序ROM用,由上位机下载进去,下载完后不再更新。RAM速度为6~12MHz)。
1. 首选怀疑是不是电路有问题
用100MHz的时钟来抓we信号,没有发现we有高电平。
2. 难道有毛刺?把we接成0,上位机不更新RAM,只在综合时将初始值置进去。
仍然会出错。
3. 多放一片一样的RAM,地址、时钟、we全接一样,两片RAM的输出做个比较电路,以检测ROM内容是否发生变化。
结果:会发生变化。即使we接0,也会。

我个人觉得,一片RAM,WE接地,但是初始化进去的数据却会随机改变,硬说是我电路的设计问题,我是无法接受的。

结论在16、21、22楼。

高手在民间啊。

厂家的技术,一会是软件问题,一会是IO问题,另外又是代码问题:
不要用parameter!
U_DLY的延时,仿真和综合结果会不一样!
1<<ADDR_WIDTH,这种写法不对!
mem文件和RAM深度不一致!

sme 发表于 2013-11-15 17:17:26



看图,两片RAM的输出,u_irom/dout和u_irom_ref/dout和code对比,有不一致的。



这个错得更多

sme 发表于 2013-11-15 17:23:21

本帖最后由 sme 于 2013-11-15 17:27 编辑

为了简单,我又做了实验,单独做了个工程,就放两片RAM和比较电路,并把它发给了厂家的技术人员。


附件里是源代码,高手可以看下。

使用这个电路,按板上的复位键,会随机出错(rom_err=1)。

为了验证是不是我的板子有缺陷,让厂家也用他们手头的板子了试验,结果是:按复位键,rom_err会随机出现1。
和我的结果一样。

证明至少两边的板子是一样的现象。

sme 发表于 2013-11-15 17:24:46



我的工程,用片内逻辑分析仪抓到的,两个RAM的输出出现不一致。

sme 发表于 2013-11-15 17:25:50

厂家的答复是:

用他们自己的代码不会出现问题,我的问题是随机现象,是设计问题。

在这里高手看一下,我的设计到底有什么问题?

sme 发表于 2013-11-15 17:29:25

rom的描述:

`define        U_DLY        1
`timescale        1ns/1ns

module        c8051f_irom (
        clk,
        addr,
        di,
        we,
        oe,
        ce,
        dout
        );

        parameter        ADDR_W        =        14;
        parameter        BUS_W        =        8;
        parameter        ROM_FILE        =        "HIDtoUART.mem";
       
        input                        clk;
        input                addr;
        input                di;
        input                        we;
        input                        oe;
        input                        ce;
        output                dout;

`ifdef        ASIC

`else
        reg                        mem        [(1<<ADDR_W)-1:0];
        reg                        rd_addr;

        initial
        begin
                $readmemh(ROM_FILE,mem);
                $display("**************************************************************");
                $display("*                                                                                                                           *");
                $display("*Read code file from file %s *",ROM_FILE);
                $display("*                                                                                                                           *");
                $display("**************************************************************");
    end

        always @(posedge clk)
        begin
                if (ce & we)
                        mem        <=        #`U_DLY di;
                rd_addr        <=        #`U_DLY addr;
        end
        assign        #`U_DLY        dout        =        (ce & oe) ? mem : 8'h00;
`endif

endmodule

lans0625 发表于 2013-11-15 17:29:51

帮顶,等高手。。。{:smile:}

sme 发表于 2013-11-15 17:30:07

本帖最后由 sme 于 2013-11-15 17:32 编辑

rom的调用:

        wire                irom_read_data;
        wire                irom_ref_data;
       
        c8051f_irom #(`ROM_ADDR_WIDTH) u_irom (
                .clk                        (rom_clk),
                .addr                        (irom_addr[`ROM_ADDR_WIDTH-1:0]),
                .di                                (8'h00),        //        irom_addr),
                .we                                (xween),   //      这引脚接到FPGA的引脚,用拔动开关,控制写信号是否有效。如果外部接到高电平,则ram内容全被全写成0,可以看到rom_err为高;当外部接低电平时,当ROM用
                .oe                                (1'b1),
                .ce                                (1'b1),
                .dout                        (irom_read_data)
        );
       
        c8051f_irom #(`ROM_ADDR_WIDTH) u_irom_ref (
                .clk                        (rom_clk),
                .addr                        (irom_addr[`ROM_ADDR_WIDTH-1:0]),
                .di                                (8'h00),
                .we                                (1'b0),    //      这个接成0,当ROM用
                .oe                                (1'b1),
                .ce                                (1'b1),
                .dout                        (irom_ref_data)
        );

sme 发表于 2013-11-15 17:30:47

两个rom 的输出进行比较:

`ifdef        ASYNC_RESET               
        always @(negedge rst_n2 or posedge rom_clk)
`else
        always @(posedge rom_clk)
`endif
        begin
                if (!rst_n2)
                        rom_err        <=        1'b0;
                else
                        rom_err        <=        irom_read_data != irom_ref_data;
        end

WM_CH 发表于 2013-11-15 17:30:57

本来不想回帖,回了也是灌水。。。{:dizzy:}

sme 发表于 2013-11-15 17:34:46

WM_CH 发表于 2013-11-15 17:30 static/image/common/back.gif
本来不想回帖,回了也是灌水。。。

电路很简单,用心去看,一定能了解。

uindex 发表于 2013-11-16 00:53:00

除了Verilog,Waveform. FPGA还有很多东西,比如你这个问题需要赶紧看timing report.
我个人猜测主要问题出在:
reg                      mem      [(1<<ADDR_W)-1:0];

这种写法,导致mem逻辑巨大。跑不到12M。

建议有三个:

[*]减小RAM尺寸,减小到1KBYE,从新实验,如果过了,那说明猜的没错。
[*]给CLK增加一条Clock约束,约到大于12M,然后再用12M做测试。
[*]芯片自带ROM/RAM的IP要用起来。

sme 发表于 2013-11-16 09:08:13

本帖最后由 sme 于 2013-11-16 09:10 编辑

uindex 发表于 2013-11-16 00:53 static/image/common/back.gif
除了Verilog,Waveform. FPGA还有很多东西,比如你这个问题需要赶紧看timing report.
我个人猜测主要问题出 ...

谢谢你的回复,个人认为并不是以上原因。

1. 关于mem的写法,这个是没有问题的,只是定义了两个参数而已,用参数没理由就导致RAM巨大。
下面是xilinx的Language Template的写法:
   parameter RAM_WIDTH = <ram_width>;
   parameter RAM_ADDR_BITS = <ram_addr_bits>;
   
   (* RAM_STYLE="{AUTO | BLOCK |BLOCK_POWER1 | BLOCK_POWER2}" *)
   reg <ram_name> [(2**RAM_ADDR_BITS)-1:0];
   reg <output_data>;

   <reg_or_wire> <address>;
   <reg_or_wire> <input_data>;

   //The following code is only necessary if you wish to initialize the RAM
   //contents via an external file (use $readmemb for binary data)
   initial
      $readmemh("<data_file_name>", <rom_name>, <begin_address>, <end_address>);

   always @(posedge <clock>)
      if (<ram_enable>) begin
         if (<write_enable>)
            <ram_name>[<address>] <= <input_data>;
         <output_data> <= <ram_name>[<address>];
      end

由上可见,同样是使用的参数定义。
定义参是为了调用方便,没有规定不能用参数吧?1<<n 和2**n是一样的。我给的ADDR_W是的14,综合出来的就是16KB,结果并没有错。

2. 减小RAM尺寸
这个上班了我会再试。

3. 增加约束。
我是从spartan3/spartan6换到artix-7,这种跑1、20M时钟的电路,很轻易。
并且,我估的这个测试工程,就2片RAM,没有理由连12M都跑不到。

再,你看我在二楼的图片,我是用的100MHz的时钟来找取ROM的信号,ROM的速度完全没有问题。

4. 用IP
之所以不用IP,是为了移值方便。我自己做的东西,除了跟物理特性有关的,像DCM、PLL等,能不用IP尽量不用IP,这样换型号最方便。


做的这个测试工程,已经是精简至极,我觉得只要RAM的WE是无效的,没有理由RAM内容会变化。

之前我是整个项目出错,找了好久才发现是RAM的问题。通过片内逻辑分析仪可以确定,时序、速度都没问题,百思不得其解,然后才想到调用两次来比较结果,确认不是电路会存在误写导致数据出错。

Fourier00 发表于 2013-11-16 10:47:08

这个地方 应该是时钟有毛刺引起的,你给了一个分频时钟,然后这个时钟可能有毛刺,导致ram的地址出现了亚稳态,然后ram里面的数据被修改了,ram应用中即使是不写使能不为1,只要是读写地址出现了亚稳态都会修改ram里面的数据,我建议LZ把时钟修改成时钟使能,而不是直接用作时钟

sme 发表于 2013-11-16 10:55:04

Fourier00 发表于 2013-11-16 10:47 static/image/common/back.gif
这个地方 应该是时钟有毛刺引起的,你给了一个分频时钟,然后这个时钟可能有毛刺,导致ram的地址出现了亚稳 ...

嗯,兄弟这句话确实有一定的道理。

我有两个疑问:
1. 分频时钟有毛刺
个人认为这不太可能。不管是用同步分频,还是使用2分频的异步串联,只要最后抽出来的时钟,是从寄存器出来,都不可能有毛刺。

2. 分频电路如下:
`ifdef        ASYNC_RESET       
        always @(negedge rst_n or posedge clk100m)
`else
        always @(posedge clk100m)
`endif
        begin
                if (!rst_n)
                        div        <=        8'h00;
                else
                        div        <=        div + 1;
        end
        assign        rom_clk        =        div;                //        100MHz/8

这种除8的电路,应该是没有毛刺的。

Fourier00 发表于 2013-11-16 10:59:55

还有一种可能就是你的异步复位没有做同步化引起的,同时时钟也不稳定,导致ram里面的数据被改写

Fourier00 发表于 2013-11-16 11:00:39


`define        ROM_ADDR_WIDTH        14
`define        ASYNC_RESET                1

module top (
        input                        clk100m,
        input                        rst,
        input                        xween                /*        synthesis xc_pulldown = 1 */,
        output                        rom_clk,
        output        reg                rom_err
        );
       
        reg                        div;
        reg                        rst_n_ff;
        wire                        rst_n;
       
       
        reg   rst_1syn;
        reg   rst_2syn;
        wirerst_n   ;
        wirerom_clk ;
        always @(posedge clk100m)
        begin
                rst_1syn <= ~rst;
                rst_2syn <= rst_1syn;
        end
        assign        rst_n        =rst_2syn;
        always @(negedge rst_n or posedge clk100m)
        begin
                if (!rst_n)
                        div        <=        8'h00;
                else
                        div        <=        div + 1;
        end
       
        assign        rom_clk        =        (div == 3'd0)?1'b1:1'b0;                //        100MHz/8
       
        reg                [`ROM_ADDR_WIDTH-1:0]        irom_addr;
       
        always @(negedge rst_n or posedge clk100m)
        begin
                if (!rst_n)
                        irom_addr        <=        0;
                else if(rom_clk == 1'b1)
                        irom_addr        <=        irom_addr + 1;
        end

        wire                irom_read_data;
        wire                irom_ref_data;
       
        c8051f_irom #(`ROM_ADDR_WIDTH) u_irom (
                .clk                        (clk),
                .addr                        (irom_addr[`ROM_ADDR_WIDTH-1:0]),
                .di                                (8'h00),        //        irom_addr),
                .we                                (xween),
                .oe                                (1'b1),
                .ce                                (1'b1),
                .dout                        (irom_read_data)
        );
       
        c8051f_irom #(`ROM_ADDR_WIDTH) u_irom_ref (
                .clk                        (clk),
                .addr                        (irom_addr[`ROM_ADDR_WIDTH-1:0]),
                .di                                (8'h00),
                .we                                (1'b0),
                .oe                                (1'b1),
                .ce                                (1'b1),
                .dout                        (irom_ref_data)
        );
       
        always @(negedge rst_n or posedge clk100m)
        begin
                if (!rst_n)
                        rom_err        <=        1'b0;
                else
                        rom_err        <=        irom_read_data != irom_ref_data;
        end
       
endmodule

sme 发表于 2013-11-16 11:01:44

Fourier00 发表于 2013-11-16 10:59 static/image/common/back.gif
还有一种可能就是你的异步复位没有做同步化引起的,同时时钟也不稳定,导致ram里面的数据被改写
...

这个可能说到点子上了,我想想。

sme 发表于 2013-11-16 11:05:33

Fourier00 发表于 2013-11-16 11:00 static/image/common/back.gif


我明白你的意思,使用使能的方式,有两个问题:
1. 与我的IC不符。
做IC时是有不少分频时钟的,做FPGA是为了ASIC的前期验证。

2. 这种方式,耗电会大不少,因为整个电路都是以最高频率在运行。当然,FPGA耗电大点无所谓。

Fourier00 发表于 2013-11-16 11:15:48

sme 发表于 2013-11-16 11:05 static/image/common/back.gif
我明白你的意思,使用使能的方式,有两个问题:
1. 与我的IC不符。
做IC时是有不少分频时钟的,做FPGA是 ...

FPGA 里面不推荐使用分频时钟的,做ASIC验证 应该有一定的规则来处理这种分频时钟的吧,比如说时钟使能,或者说分频出去在上锁相环然后上全局,我没有做过ASIC验证,所以不知道你们这些是怎么处理的,还有就是分频时钟其实分频了反转率降低了,动态功耗会下降,但是相比与直接的时钟降频来说,静态功耗还是会要高些

sme 发表于 2013-11-16 11:18:38

本帖最后由 sme 于 2013-11-16 11:32 编辑

Fourier00 发表于 2013-11-16 10:59 static/image/common/back.gif
还有一种可能就是你的异步复位没有做同步化引起的,同时时钟也不稳定,导致ram里面的数据被改写
...

你说的这点,可能要从RAM的电路结构入手。

我曾经反向过由foundry提供的memory compiler出来的RAM,其原理、结构可能和FPGA里的block RAM一样。

同步RAM的信号,通常是CLK、CE、OE、WE、ADDR、DI。
OE在这里不考虑,只控制输出的buffer。
其它的信号,利用CLK来采样。
CLK的处理,最为关键。我看到的电路,是利用CLK产生一极窄的单击脉冲,来锁存输入信号。
ADDR、DI等输入信号的变化,在CLK边沿无效时,是不会影响电路的任何功能。
但在CLK的单击脉冲这段时间内,如果WE无效,是作读操作,由ADDR来确定选中的RAM单元。
一个RAM单元一般都是6T结构,同一时刻,其bit line选中的同一列中,只能选中一行,如果选中了多行,就可能会改写数据。
如果此时ADDR在变化,就会出现word line选择的毛刺,可能有多行被选中。

回到我的电路:
rom_clk是12.5MHz,其地址线也是由rom_clk计数产生,因此不管地址和时钟是用同一沿,或是分别用上升、下降沿,其setup/hold都满足。
并且,rom_clk应该也无毛刺。

问题出在复位上。
当异步复位发生时,有可能正好发生在rom_clk的有效沿,而此时ADDR也会复位。这一组ADDR在此时的变化,会导致Setup/hold不满足,从而使RAM的WE即使无效,其数据也有可能被改。

{:sweat:}

Fourier00 发表于 2013-11-16 11:45:49

亚稳态修改ram

sme 发表于 2013-11-16 11:54:48

Fourier00 发表于 2013-11-16 11:45 static/image/common/back.gif
亚稳态修改ram

嗯,应该就是这个问题。{:handshake:}

奮闘ing 发表于 2013-11-16 16:16:06

菜鸟一枚,只是来学习的!谢谢高手分享~

jm2011 发表于 2013-11-16 22:33:45

楼主公布一下修改后的测试结果把;

sme 发表于 2013-11-17 08:06:59

jm2011 发表于 2013-11-16 22:33 static/image/common/back.gif
楼主公布一下修改后的测试结果把;

已经通过测试,就是异步复位的原因。

utopianism 发表于 2013-11-17 08:26:28

菜鸟观摩了!{:sweat:}

winster321 发表于 2013-11-17 09:09:43

呵呵,异步复位同步释放。话说楼主不是在广告区做mcu ic定制的那位?

sme 发表于 2013-11-17 09:32:52

winster321 发表于 2013-11-17 09:09 static/image/common/back.gif
呵呵,异步复位同步释放。话说楼主不是在广告区做mcu ic定制的那位?

嗯,是的。

这个问题,异步复位、同步释放,也是解决不了的。和异步复位、异步释放比起来,只是将出错机率减少了50%。
页: [1]
查看完整版本: 高手看看,能不能确定是FPGA板的问题?有代码有波形