- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page