Community
cancel
Showing results for 
Search instead for 
Did you mean: 
317 Views

Why would these two clear on reads behave differently?

Jump to solution

Device: Cyclone IV EP4CE15F17C8N

HDL: Verilog

 

We have a register (status_reg) in our FPGA that we want to clear when we read (IE push data it to SPI buffer). When we read this register, we & it with a bit pattern to clear the desired bits (example A). This has been working as expected, although we don’t know why.

 

We tried to add a second register (error_reg) and clear it upon being read, this time clearing every bit (example B). We even chose to & the register with zeros for consistency with how we handle the other register. However when we did this, the SPI would always read 0, even though the debugger showed us that the register was being set and reset. For some reason, the data is not coming through the SPI. Using different bit patterns did not change the behavior.

 

So why does example A work as expected, and example B does not? Why might the two give different behaviors despite using similar logics? Is there a race between setting and clearing the register?

 

...

output reg [15:0] spi_out,

output reg [15:0] status_reg,

output reg [15:0] error_reg,

...

 

// Example A: works as expected

A:          

begin

spi_out <= status_reg;

status_reg <= (status_reg & 16'b1110000000110001);

end

 

// Example B: spi_out always returns zeros

B:          

begin

spi_out <= error_reg;

error_reg <= (error_reg & 16'b0000000000000000);

end

 

Our concern is that for one register it works and the other it does not. It looks like it could be a timing issue with the clear happening before the data is actually sent out on the SPI bus. We enclosed the two non-blocking statements in a begin/end to make them sequential. And even tested out making them blocking to force the value transfer to the data bus before we clear the bits, but that had no effect either.

0 Kudos
1 Solution
ak6dn
Valued Contributor II
118 Views

Well, that looks basically ok.

I would have to assume that input CS is only valid for a single clock cycle. Correct?

Or if CS is valid for more than one clock cycle then SPI_RD is only valid for one clock cycle.

Otherwise if CS and/or SPI_RD is present for two (or more cycles) the register will be read once, then cleared, then read again with the value zero.

View solution in original post

5 Replies
ak6dn
Valued Contributor II
118 Views

What is the trigger to execute block A vs block B? You don't show it.

 

SPI is a serial bitstream, so it will take some time to shift the data out to it.

Is 'spi_out' also used as the shift register, or is it loaded at some point into the actual SPI shift register.

 

Without having more context it is not possible to determine why you see this behavior.

Taken in isolation, blocks A and B should work, given that each block is just triggered once per observation.

118 Views

The clear-on-read blocks are driven by the 100MHz FPGA clock, and are triggered when the cs and the spi_rd flag are set. The switch case selects which register is read(and cleared), based on the address value (adr) received over SPI.

 

always @(posedge clk)

begin

if (cs==1) begin 

if (spi_rd==1) begin

case (adr)

0: begin

spi_out <= status_reg;

status_reg <= (status_reg & 16'b1110000000110001);

end

1: begin

spi_out <= error_reg;

error_reg <= (error_reg & 16'b0000000000000000);

end

endcase

end

end

//data is loaded into status_reg and error_reg here

end  

 

The spi_out register is loaded into the actual shift register before being transmitted. We are using Altera's SPI IP and have had no problems with it thus far.

 

ak6dn
Valued Contributor II
119 Views

Well, that looks basically ok.

I would have to assume that input CS is only valid for a single clock cycle. Correct?

Or if CS is valid for more than one clock cycle then SPI_RD is only valid for one clock cycle.

Otherwise if CS and/or SPI_RD is present for two (or more cycles) the register will be read once, then cleared, then read again with the value zero.

View solution in original post

118 Views

So it looks like SPI_RD was high for too many cycles, and the data were being read, then cleared, then read again like you described.

We added logic to only clear status_reg after the register had been read by the SPI.

To do this, we separated the two operations into two SPI commands.

Since status_reg is continuously updated, we added status_temp to preserve any new statuses that come in during the time after we read the status_reg but before we clear it.

 

//during read

begin data_out <= status_reg[15:0]; status_temp[15:0] <= status_reg[15:0]; end

 

//after read

status_reg <= (status_reg[15:0] ^ status_temp[15:0])|(status_reg[15:0] & 16'b1110000000110001);

 

Thanks for your help!

ak6dn
Valued Contributor II
118 Views

I have come across this kind of problem before.

The way I handled it was to basically design a 'positive edge' detector to qualify the use of the signal, so that it is only qualified the first clock period it goes high.

 

So, register the signal to delay it one clock: always @(posedge clk) csd1 <= cs;

then whenever you use cs later, use: cs & ~csd1 instead.

Reply