Go Down

Topic: Vidor for simple counters (Read 856 times) previous topic - next topic

jss87

This board looks interesting for an application that is probably too much for a standard Arduino.

I have 8 lines of pulses that need to be counted. Each pulse is 0.5 us wide. The gap between pulses could be anything but being able to count pulses that are 0.2 us apart should work.

I would like to read all 8 counters every ms or so. I read elsewhere in this forum that a read of an encoder count takes about 1.5 ms. Is that still the case? I guess this would not improve if simple counters rather than quadrature encoders are used.

If the read times are very stable, I might be able to use the system but 8 X 1.5 = 12 ms is using up a lot of the time I have to act on the counts.

Does anyone have recommendations?


Thanks


Limba

I think that would be nice "example" case where this can be used.

For Minimum 10 MHz clock for detection logic is needed. That for detecting 0.1us delay between pulses.
Also you can use pregenerated 100MHz clock from main PLL for that.

Do you need only pulses within 1ms period or is period configurable?
I think if you use 10 MHz SPI for communication between FPGA and MCU then tranfer all 8 counters 32bit (20-24 bit count data + stats) is about 25.6 us + SW time in MCU side.

Current example designs have mailbox connected to JTAG-AvalonMM IP. So MCU will communicate with JTAG to FPGA. I don't know current speed of JTAG and how much header there is for AvalonMM commands.

Other solution is to use Nios II inside FPGA instead or MCU for calculation but Arduino IDE isn't supporting it now so you have to use native code but you have 8MB SDRAM in use.

DarioPennisi

hi,
i haven't recently measured a jtag transaction but it should be much faster than 1.5ms. that really depends on what you're doing anyway as the current FPGAs don't allow accessing peripheral registers directly but rather do it indirectly though the softcore so in order to read a counter you're asking the softcore to do that for you and then you're waiting for the response.
if you need a faster interaction you can start from the projects you find in github and modify them so that you can access peripheral registers directly from JTAG bridge. that is very simple and amounts to just loading the qsys project and connecting the quad encoder slave bus also to the jtag bridge.
once done this you also have to slightly modify the code on arduino side so that you can read registers directly and that should do the trick...

philippe_at_sysemb

#3
Dec 30, 2018, 05:54 pm Last Edit: Dec 30, 2018, 06:25 pm by philippe_at_sysemb
Hi jss87,

The FPGA is well suited for your use. I think it's better to use "asynchronous" (not link to an internal clock)  logic to measure frequencies up to 300Mhz (3 ns pulse width).
To transfer the counters values from the FPGA, SPI protocol is a good solution. SAMD21 should accept rate above 20Mbps.

For your question, here a code example :

Code: [Select]

module Counters #(

parameter pCOUNT_SIGNALS = 8, // Number of input signals
parameter pSPI_DEVICE = 1 // SPI device address
 )
 (
 
// Clock Signals
input [pCOUNT_SIGNALS-1:0] iCOUNT_CLK,
 
// SPI Signals
input [4:0] iSPI_PERIPH_SLCT, // SPI device address
input iSPI_READ_SIG, // Read signal
input iSPI_INC_RDADDR,   // Increment SPI Read Address

output [7:0] oSPI_DATA // Data to send to SPI


 );


reg [15:0] CounterX[pCOUNT_SIGNALS - 1:0];
reg [15:0] HoldCounterX[pCOUNT_SIGNALS - 1:0];

wire wREAD_REQ; assign wREAD_REQ = (pSPI_DEVICE == iSPI_PERIPH_SLCT) ? 1 : 0;

generate
genvar ii;
for (ii = 0; ii < pCOUNT_SIGNALS; ii = ii +1 ) begin : CNT
always @(posedge iCOUNT_CLK[ii])
begin
CounterX[ii] <= CounterX[ii] + 1;
if (wREAD_REQ == 0)
HoldCounterX[ii] <= CounterX[ii] + 1;
end
end
endgenerate


// SPI Part

reg [3:0] AddrRead; // Address of SPI read

// Increment Read address
always @(posedge iSPI_INC_RDADDR or negedge wREAD_REQ)
begin
if (wREAD_REQ == 1'b0)
AddrRead <= 4'b0;
else
AddrRead <= AddrRead + 1'b1;
end

assign oSPI_DATA = (iSPI_READ_SIG == 0) ? 8'bZ : ((AddrRead[0] == 1'b0) ? HoldCounterX[AddrRead/2][7:0] : HoldCounterX[AddrRead/2][15:8]);


endmodule


The counters (CounterX[channel]) are increment on positive edges of channel input (posedge iCOUNT_CLK[channel]). Values for each counters are hold in specific registers (HoldCounterX[channel]) during SPI read access.

It uses the SPI module I developped in one of my tutorial (uncomment lines with oSPI_READ_SIG and oSPI_INC_RDADDR) :
Programming FPGA for MKR VIDOR4000 (PWM)

You can also find an example for the ARDUINO Sketch on my download page.

Philippe

Limba

#4
Dec 31, 2018, 12:44 pm Last Edit: Dec 31, 2018, 01:12 pm by Limba
For your question, here a code example :

Code: [Select]

*** Snip ***
 wire wREAD_REQ; assign wREAD_REQ = (pSPI_DEVICE == iSPI_PERIPH_SLCT) ? 1 : 0;

 generate
   genvar ii;
   for (ii = 0; ii < pCOUNT_SIGNALS; ii = ii +1 ) begin : CNT
     always @(posedge iCOUNT_CLK[ii])  -- Comment 1
     begin
       CounterX[ii] <= CounterX[ii] + 1;
       if (wREAD_REQ == 0)                    -- Comment 2
         HoldCounterX[ii] <= CounterX[ii] + 1;
     end
   end
 endgenerate

*** Snip ***


Comment 1:
It can be harder to get this in timing without using dedicated clock routing resources.

Comment 2:
That pretty dangerous CDC there.
I would recommend to sync wREAD_REQ signal and add quard delay check for SPI or atleast use gray code counter there.

When wREAD_REQ and destination clock rise at same time then routing delays and some other parameters will effect which will cause some of registers from HoldCounterX will take next value and some will keep old value. Using gray code only one bit will change by 1 count. so error is max to 1 but with normal counting style error can be big.
Example: If value is changing from 0x7FF to 0x800 value can be 0xXXX where X is random bit.

Edit:
Typos.
https://en.wikipedia.org/wiki/Gray_code
https://www.eetimes.com/document.asp?doc_id=1278809  -- Part 1
https://www.eetimes.com/document.asp?doc_id=1278827  -- Part 2


DarioPennisi

Hi,
tend to agree with Limba. Philippe's code is good from a conceptual point of view but not that good in practice as it hides some trouble. generally speaking in FPGAs it's always important to resync everything to a system clock so that logic is completely synchronous and you don't have to deal with metastable states. if you look at my original code for the encoders you'll see the first thing i do is synchronize input signals to system clock, then i compare old states with new to determine if there was an edge have counters increment or decrement on system clock so that registers are all synchronouse with the same clock.
in the same way, tiny spi IP i took from OpenCores syncrhonizes SCK and other signals to system clock...
although it's not immediate to see why, what limba said it's true... you can't rely on clock transfers without proper synchronization and even if you do it, there will be cases in which just synchronizing multiple bits won't work.
more in general... it's ok to synchronize a single bit by using a shift register and taking into account that in some (very rare) cases a transition may cause a double pulse. in case of registers with multiple bits, synchronization should always be done via FIFOs so that you read data only when you're sure it's been stable for a while.

Limba

I think FIFO is needed if you need to pass datastream or burst of data to another clockdomain. Normal handshaking should be fine for single multibit values.

Also FIFO use internally gray code counter for memory pointers. Like checking if FIFO is empty or full.

Other thing that I forgot to mention is use of initial values or reset signal. Registers without initial value or reset will give X in simulation.

If reset routing start to take too much of resources in FPGA then you can only reset output registers, control logic, state macines, and use initial values for datapath registers. In this case flush functionality is preferred that we can clean datapath after new reset situation.

jss87

To all,

I must apologize for not replying sooner; I should check the forum settings as I received no notices of action on the thread.

This all sounds encouraging enough to buy one of the boards and see what happens.

I could have been more clear: I am not reading an encoder; it's more like counting pulses from a Geiger counter. I would prefer not to miss counting a pulse if occurs during a read operation but it sounds like that will be very rare and would be tolerable.

Thanks for the ideas.

John


Go Up