Search code examples
verilogzynq

Verilog module generates impossible data when running on FPGA


I’m developing a boolean data logger on a ZYNQ 7000 SoC. The logger takes a boolean input from GPIO and logs the input’s value and the time it takes to flip.

I use a 32-bit register as a log entry, the MSB bit is the boolean value. The 30:0 bits is an unsigned integer which records the time between last 2 flips. The logger should work like the following picture.

logger

Here's my implementation of the logger in Verilog. To read the logged data from the processor, I use an AXI slave interface generated by vivado and inline my logger in the AXI module.

module BoolLogger_AXI #(
    parameter BufferDepth = 512
)(
    input wire data_in, // boolean input
    input wire S_AXI_ACLK, // clock
    input wire S_AXI_ARESETN, // reset_n
    // other AXI signals
);
wire slv_reg_wren; // write enable of AXI interface
reg[31:0] buff[0:BufferDepth-1];

reg[15:0] idx;
reg[31:0] count;
reg last_data;
always @(posedge S_AXI_ACLK) begin
    if((!S_AXI_ARESETN) || slv_reg_wren) begin
        idx <= 0;
        count <= 1;
        last_data <= data_in;
    end else begin
        if(last_data!=data_in) begin // add an entry only when input flips
            last_data <= data_in;
            if(idx < BufferDepth) begin // stop logging if buffer is full
                buff[idx] <= count | (data_in << 31);
                idx <= idx + 1;
            end
            count <= 1;
        end else begin
            count <= count + 1;
        end
    end
end

//other AXI stuff
endmodule

block design

In the AXI module, the 512*32bit logged data is mapped to addresses from 0x43c20000 to 0x43c20800.

In the Verilog code, the logger adds a new entry only when the boolean input flips. In simulation, the module works as expected. But in the FPGA, sometimes the logged data is not valid. There are successive 2 data and their MSB bit is the same, which means the entry is added even when the boolean input stays the same.

logged data

The invalid data appear from time to time. I've tried reading from the address programmatically (*(u32*)(0x43c20000+4*idx)), and there are still invalid data. I watch idx in a ILA module and idx is 512, which means the logging finishes when I read the data.

The FPGA clock is 10 MHz. The input signal is 10 Hz. So the typical period is 10e6/10/2=0x7A120, which most of the data is close to, except the invalid data.

I think if the Verilog code is implemented well, there should be no such invalid data. What may be the problem? Is this an issue about timing?

The code


Solution

  • First off, are you absolutely sure you are not issuing an accidental write on the AXI bus, resetting the registers?

    If so, have you tried inserting a so-called double-flop on data_in (two flip-flops, delaying the signal two clock ticks)? I suppose that your data_in is not synchronous to the FPGA clock, which will lead to metastability and you having bad days if not accounted for. Have a look here for information by NANDLAND.

    Citing the linked source:

    If you have ever tried to sample some input to your FPGA, such as a button press, or if you have had to cross clock domains, you have had to deal with Metastability. A metastable state is one in which the output of a Flip-Flop inside of your FPGA is unknown, or non-deterministic. When a metastable condition occurs, there is no way to tell if the output of your Flip-Flop is going to be a 1 or a 0. A metastable condition occurs when setup or hold times are violated.

    Metastability is bad. It can cause your FPGA to exhibit very strange behavior.

    In that source there is also a link to a white paper from Altera about the topic of metastability, linked here for reference.

    Citing from that paper:

    When a metastable signal does not resolve in the allotted time, a logic failure can result if the destination logic observes inconsistent logic states, that is, different destination registers capture different values for the metastable signal.

    and

    To minimize the failures due to metastability in asynchronous signal transfers, circuit designers typically use a sequence of registers (a synchronization register chain or synchronizer) in the destination clock domain to resynchronize the signal to the new clock domain. These registers allow additional time for a potentially metastable signal to resolve to a known value before the signal is used in the rest of the design.

    Basically having the asynchronous signal routed to two flip-flops might for example lead to one FF reading a 1 and one FF reading a 0. This in turn could lead to the data point being saved, but the counter not being reset to 0 (hence doubling the measured time) and the bit being saved as 0.

    Finally, it seems to me, that you are using the Vivado-generated example AXI core. Dan Gisselquist simply calls it "broken". This might not be the problem here, but you might want to have a look at his posts and his AXI core design.