ID |
Date |
Author |
Subject |
907
|
Thu Feb 22 10:37:03 2024 |
Stefan Ritt | Simulation of FPGA | The Cypress has its own firmware, contained in the distribution under firmware/CY7C68013A/drs_eval.c. There you can see how the data is fetched. I kind of forgot how exactly it worked, since I wrote that code back in 2011. But most if the Cypress code is just the configuration of the USB, the communication with the FPGA is kind of straight forward in the Cypress implementation. But you have to read the manual of that chip to understand it.
Unfrtunately there is no full testbench for the firmware, since I didn't have a VHDL Model of the Cypress, so I implemente dit the "hard" way ;-)
Best,
Stefan
Rod McInnis wrote: |
Hello:
A bit of background: I am working on a project that is utilizing the DRS4 Evaluation board as a prototype platform for a dedicated, special use capture. We will only be utilizing one channel of the ADC capture, and the 1024 samples is more than enough.
What I will need to do, however, is do some preprocessing on the incoming ADC data, running some calculation on the fly, possibly some filtering and other transformations before putting the data into the FPGA block memory for transfer to the host via the Cypress USB interface. I will be modifying the "drs4_eval5" VHDL file and doing a new FPGA build.
It will be essential that I be able to simulate this, from the ADC input to the data flow to the Cypress chip. I have "eval board files" which includes the VHDL source files, Xilinxe ISE project files and some very basic simulation testbenches.
Unfortunately, the simulation testbenches call out a "drs4_eval1" module while the Xilinx project uses a "drs4_eval5" module, and the module ports are a little different. I think I can work around that, however. I have run the simulatilon "drs4_eval1_tb", which does a simple write to a Control Register. I need to expand this simulation so that it will initiate a full capture and then transfer the data from the RAM to the Cypress chip.
What I am most confused about is how the Cypress chip sucks out the data from the FPGA block ram. I would expect it to use a burst mode data transfer rather than the cumbersom CSR read/write, but I haven't found any documentation on how this interface works.
Q1: Is there a simulation testbench file available that does the 1024 sample data transfer?
Q2: Is there a waveform diagram that shows the protocol / signal handshake between the FPGA and Cypress chip for this data transfer?
Thank you
Rod McInnis
|
|
7
|
Tue Apr 28 11:44:07 2009 |
Stefan Ritt | Simple example application to read a DRS evaluation board | Several people asked for s simple application to guide them in writing their own application to read out a DRS board. Such an application has been added in software revions 2.1.1 and is attached to this message. This example program drs_exam.cpp written in C++ does the following necessary steps to access a DRS board:
- Crate a "DRS" object and scan all USB devices
- Display found DRS boards
- Initialize the first found board and set the sampling frequency to 5 GSPS
- Enable internal trigger on channel #1 with 250 mV threshold
- Start acquisition and wait for a trigger
- Read two waveforms (both time and amplitude)
- Repeat this 10 times
I know that we are still missing a good documentation for the DRS API, but I have not yet found the time to do that. I hope the example program is enough for most people to start writing own programs. For Windows users (MS Visual C++ 8.0) there is a drs.sln project file, and for linux users there is a Makefile which can be used to compile this example program.
|
Attachment 1: drs_exam.cpp
|
/********************************************************************\
Name: drs_exam.cpp
Created by: Stefan Ritt
Contents: Simple example application to read out a DRS4
evaluation board
$Id: drs_exam.cpp 13344 2009-04-28 07:34:45Z ritt@PSI.CH $
\********************************************************************/
#include <math.h>
#ifdef _MSC_VER
#include <windows.h>
#elif defined(OS_LINUX)
#define O_BINARY 0
#include <unistd.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <errno.h>
#define DIR_SEPARATOR '/'
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "strlcpy.h"
#include "DRS.h"
/*------------------------------------------------------------------*/
int main()
{
int i, j, nBoards;
DRS *drs;
DRSBoard *b;
float time_array[1024];
float wave_array[8][1024];
/* do initial scan */
drs = new DRS();
/* show any found board(s) */
for (i=0 ; i<drs->GetNumberOfBoards() ; i++) {
b = drs->GetBoard(i);
printf("Found DRS4 evaluation board, serial #%d, firmware revision %d\n",
b->GetBoardSerialNumber(), b->GetFirmwareVersion());
}
/* exit if no board found */
nBoards = drs->GetNumberOfBoards();
if (nBoards == 0) {
printf("No DRS4 evaluation board found\n");
return 0;
}
/* continue working with first board only */
b = drs->GetBoard(0);
/* initialize board */
b->Init();
/* set sampling frequency */
b->SetFrequency(5);
/* enable transparent mode needed for analog trigger */
b->SetTranspMode(1);
/* use following line to disable hardware trigger */
//b->EnableTrigger(0, 0);
/* use following line to enable external hardware trigger (Lemo) */
//b->EnableTrigger(1, 0);
/* use following lines to enable hardware trigger on CH1 at 250 mV positive edge */
b->EnableTrigger(0, 1); // lemo off, analog trigger on
b->SetTriggerSource(0); // use CH1 as source
b->SetTriggerLevel(0.25, false, 0); // 0.25 V, positive edge, zero delay
/* repeat ten times */
for (j=0 ; j<10 ; j++) {
/* start board (activate domino wave) */
b->StartDomino();
/* wait for trigger */
printf("Waiting for trigger...");
while (b->IsBusy());
/* read all waveforms */
b->TransferWaves(0, 8);
/* read time (X) array in ns */
b->GetTime(0, time_array);
/* decode waveform (Y) array first channel in mV */
b->GetWave(0, 0, wave_array[0]);
/* decode waveform (Y) array second channel in mV*/
b->GetWave(0, 1, wave_array[1]);
/* process waveform: add here some code to display or save waveform X=time[i], Y=wave_array[n][i] */
/* print some progress indication */
printf("\rEvent #%d read successfully\n", j);
}
/* delete DRS object -> close USB connection */
delete drs;
}
|
8
|
Wed Apr 29 07:57:33 2009 |
Stefan Ritt | Simple example application to read a DRS evaluation board |
Stefan Ritt wrote: |
Several people asked for s simple application to guide them in writing their own application to read out a DRS board. Such an application has been added in software revions 2.1.1 and is attached to this message. This example program drs_exam.cpp written in C++ does the following necessary steps to access a DRS board:
- Crate a "DRS" object and scan all USB devices
- Display found DRS boards
- Initialize the first found board and set the sampling frequency to 5 GSPS
- Enable internal trigger on channel #1 with 250 mV threshold
- Start acquisition and wait for a trigger
- Read two waveforms (both time and amplitude)
- Repeat this 10 times
I know that we are still missing a good documentation for the DRS API, but I have not yet found the time to do that. I hope the example program is enough for most people to start writing own programs. For Windows users (MS Visual C++ 8.0) there is a drs.sln project file, and for linux users there is a Makefile which can be used to compile this example program.
|
One note: The program drs_exam.cpp published in the previous message needs the current version of the DRS library in DRS.cpp and DRS.h. They are contained in the software release 2.1.1 which has to be downloaded. For simplicity, I attached the two files to this message. |
Attachment 1: DRS.cpp
|
/********************************************************************
Name: DRS.cpp
Created by: Stefan Ritt, Matthias Schneebeli
Contents: Library functions for DRS mezzanine and USB boards
$Id: DRS.cpp 13351 2009-04-28 11:12:54Z ritt@PSI.CH $
\********************************************************************/
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <algorithm>
#include <sys/stat.h>
#include "strlcpy.h"
#ifdef _MSC_VER
#pragma warning(disable:4996)
# include <windows.h>
# include <direct.h>
#else
# include <unistd.h>
# include <sys/time.h>
inline void Sleep(useconds_t x)
{
usleep(x * 1000);
}
#endif
#ifdef _MSC_VER
#include <conio.h>
#define drs_kbhit() kbhit()
#else
#include <sys/ioctl.h>
int drs_kbhit()
{
int n;
ioctl(0, FIONREAD, &n);
return (n > 0);
}
static inline int getch()
{
return getchar();
}
#endif
#include <DRS.h>
#ifdef _MSC_VER
extern "C" {
#endif
#include <mxml.h>
#ifdef _MSC_VER
}
#endif
/*---- minimal FPGA firmvare version required for this library -----*/
const int REQUIRED_FIRMWARE_VERSION_DRS2 = 5268;
const int REQUIRED_FIRMWARE_VERSION_DRS3 = 6981;
const int REQUIRED_FIRMWARE_VERSION_DRS4 = 13191;
/*---- VME addresses -----------------------------------------------*/
#ifdef HAVE_VME
/* assuming following DIP Switch settings:
SW1-1: 1 (off) use geographical addressing (1=left, 21=right)
SW1-2: 1 (off) \
SW1-3: 1 (off) > VME_WINSIZE = 8MB, subwindow = 1MB
SW1-4: 0 (on) /
SW1-5: 0 (on) reserverd
SW1-6: 0 (on) reserverd
SW1-7: 0 (on) reserverd
SW1-8: 0 (on) \
|
SW2-1: 0 (on) |
SW2-2: 0 (on) |
SW2-3: 0 (on) |
SW2-4: 0 (on) > VME_ADDR_OFFSET = 0
SW2-5: 0 (on) |
SW2-6: 0 (on) |
SW2-7: 0 (on) |
SW2-8: 0 (on) /
which gives
VME base address = SlotNo * VME_WINSIZE + VME_ADDR_OFFSET
= SlotNo * 0x80'0000
*/
#define GEVPC_BASE_ADDR 0x00000000
#define GEVPC_WINSIZE 0x800000
#define GEVPC_USER_FPGA (GEVPC_WINSIZE*2/8)
#define PMC1_OFFSET 0x00000
#define PMC2_OFFSET 0x80000
#define PMC_CTRL_OFFSET 0x00000 /* all registers 32 bit */
#define PMC_STATUS_OFFSET 0x10000
#define PMC_FIFO_OFFSET 0x20000
#define PMC_RAM_OFFSET 0x40000
#endif // HAVE_VME
/*---- USB addresses -----------------------------------------------*/
#define USB_TIMEOUT 1000 // one second
#ifdef HAVE_USB
#define USB_CTRL_OFFSET 0x00 /* all registers 32 bit */
#define USB_STATUS_OFFSET 0x40
#define USB_RAM_OFFSET 0x80
#define USB_CMD_IDENT 0 // Query identification
#define USB_CMD_ADDR 1 // Address cycle
#define USB_CMD_READ 2 // "VME" read <addr><size>
#define USB_CMD_WRITE 3 // "VME" write <addr><size>
#define USB_CMD_READ12 4 // 12-bit read <LSB><MSB>
#define USB_CMD_WRITE12 5 // 12-bit write <LSB><MSB>
#define USB2_CMD_READ 1
#define USB2_CMD_WRITE 2
#define USB2_CTRL_OFFSET 0x00000 /* all registers 32 bit */
#define USB2_STATUS_OFFSET 0x10000
#define USB2_FIFO_OFFSET 0x20000
#define USB2_RAM_OFFSET 0x40000
#endif // HAVE_USB
/*---- Register addresses ------------------------------------------*/
#ifndef T_CTRL
#define T_CTRL 1
#define T_STATUS 2
#define T_RAM 3
#define T_FIFO 4
#endif
#define REG_CTRL 0x00000 /* 32 bit control reg */
#define REG_DAC_OFS 0x00004
#define REG_DAC0 0x00004
#define REG_DAC1 0x00006
#define REG_DAC2 0x00008
#define REG_DAC3 0x0000A
#define REG_DAC4 0x0000C
#define REG_DAC5 0x0000E
#define REG_DAC6 0x00010
#define REG_DAC7 0x00012
#define REG_CHANNEL_CONFIG 0x00014 // low byte
#define REG_CONFIG 0x00014 // high byte
#define REG_CHANNEL_SPAN 0x00016
#define REG_FREQ_SET_HI 0x00018 // DRS2
#define REG_FREQ_SET_LO 0x0001A // DRS2
#define REG_TRG_DELAY 0x00018 // DRS4
#define REG_FREQ_SET 0x0001A // DRS4
#define REG_TRIG_DELAY 0x0001C
#define REG_LMK_MSB 0x0001C // DRS4 Mezz
#define REG_CALIB_TIMING 0x0001E // DRS2
#define REG_EEPROM_PAGE 0x0001E // DRS4
#define REG_LMK_LSB 0x0001E // DRS4 Mezz
#define REG_MAGIC 0x00000
#define REG_BOARD_TYPE 0x00002
#define REG_STATUS 0x00004
#define REG_RDAC_OFS 0x0000E
#define REG_RDAC0 0x00008
#define REG_STOP_CELL0 0x00008
#define REG_RDAC1 0x0000A
#define REG_STOP_CELL1 0x0000A
#define REG_RDAC2 0x0000C
#define REG_STOP_CELL2 0x0000C
#define REG_RDAC3 0x0000E
#define REG_STOP_CELL3 0x0000E
#define REG_RDAC4 0x00000
#define REG_RDAC5 0x00002
#define REG_RDAC6 0x00014
#define REG_RDAC7 0x00016
#define REG_EVENTS_IN_FIFO 0x00018
#define REG_EVENT_COUNT 0x0001A
#define REG_FREQ1 0x0001C
#define REG_FREQ2 0x0001E
#define REG_TEMPERATURE 0x00020
#define REG_TRIGGER_BUS 0x00022
#define REG_SERIAL_BOARD 0x00024
#define REG_VERSION_FW 0x00026
/*------------------------------------------------------------------*/
using namespace std;
#ifdef HAVE_USB
#define USB2_BUFFER_SIZE (1024*1024+10)
unsigned char static *usb2_buffer = NULL;
#endif
/*------------------------------------------------------------------*/
DRS::DRS()
: fNumberOfBoards(0)
#ifdef HAVE_VME
, fVmeInterface(0)
#endif
{
#ifdef HAVE_USB
MUSB_INTERFACE *usb_interface;
#endif
#if defined(HAVE_VME) || defined(HAVE_USB)
int index = 0, i = 0;
#endif
memset(fError, 0, sizeof(fError));
#ifdef HAVE_VME
unsigned short type, fw, magic, serial, temperature;
mvme_addr_t addr;
if (mvme_open(&fVmeInterface, 0) == MVME_SUCCESS) {
mvme_set_am(fVmeInterface, MVME_AM_A32);
mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
/* check all VME slave slots */
for (index = 2; index <= 21; index++) {
/* check PMC1 */
addr = GEVPC_BASE_ADDR + index * GEVPC_WINSIZE; // VME board base address
addr += GEVPC_USER_FPGA; // UsrFPGA base address
addr += PMC1_OFFSET; // PMC1 offset
mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
i = mvme_read(fVmeInterface, &magic, addr + PMC_STATUS_OFFSET + REG_MAGIC, 2);
if (i == 2) {
if (magic != 0xC0DE) {
printf("Found old firmware, please upgrade immediately!\n");
fBoard[fNumberOfBoards] = new DRSBoard(fVmeInterface, addr, (index - 2) << 1);
fNumberOfBoards++;
} else {
/* read board type */
mvme_read(fVmeInterface, &type, addr + PMC_STATUS_OFFSET + REG_BOARD_TYPE, 2);
type &= 0xFF;
if (type == 2 || type == 3 || type == 4) { // DRS2 or DRS3 or DRS4
/* read firmware number */
mvme_read(fVmeInterface, &fw, addr + PMC_STATUS_OFFSET + REG_VERSION_FW, 2);
/* read serial number */
mvme_read(fVmeInterface, &serial, addr + PMC_STATUS_OFFSET + REG_SERIAL_BOARD, 2);
/* read temperature register to see if CMC card is present */
mvme_read(fVmeInterface, &temperature, addr + PMC_STATUS_OFFSET + REG_TEMPERATURE, 2);
/* LED blinking */
#if 0
do {
data = 0x00040000;
mvme_write(fVmeInterface, addr + PMC_CTRL_OFFSET + REG_CTRL, &data, sizeof(data));
mvme_write(fVmeInterface, addr + PMC2_OFFSET + PMC_CTRL_OFFSET + REG_CTRL, &data,
sizeof(data));
Sleep(500);
data = 0x00000000;
mvme_write(fVmeInterface, addr + PMC_CTRL_OFFSET + REG_CTRL, &data, sizeof(data));
mvme_write(fVmeInterface, addr + PMC2_OFFSET + PMC_CTRL_OFFSET + REG_CTRL, data,
sizeof(data));
Sleep(500);
} while (1);
#endif
if (temperature == 0xFFFF) {
//printf("slot %d, fw %d, no CMC board in upper slot\n", index, fw);
} else {
//printf("slot %d, fw %d, CMC serial %d in upper slot\n", index, fw, serial);
fBoard[fNumberOfBoards] = new DRSBoard(fVmeInterface, addr, (index - 2) << 1);
if (fBoard[fNumberOfBoards]->HasCorrectFirmware())
fNumberOfBoards++;
else
sprintf(fError, "Wrong firmware version: board has %d, required is %d\n",
fBoard[fNumberOfBoards]->GetFirmwareVersion(),
fBoard[fNumberOfBoards]->GetRequiredFirmwareVersion());
}
}
}
}
/* check PMC2 */
addr = GEVPC_BASE_ADDR + index * GEVPC_WINSIZE; // VME board base address
addr += GEVPC_USER_FPGA; // UsrFPGA base address
addr += PMC2_OFFSET; // PMC2 offset
mvme_set_dmode(fVmeInterface, MVME_DMODE_D16);
i = mvme_read(fVmeInterface, &fw, addr + PMC_STATUS_OFFSET + REG_MAGIC, 2);
if (i == 2) {
if (magic != 0xC0DE) {
printf("Found old firmware, please upgrade immediately!\n");
fBoard[fNumberOfBoards] = new DRSBoard(fVmeInterface, addr, (index - 2) << 1 | 1);
fNumberOfBoards++;
} else {
... 5173 more lines ...
|
Attachment 2: DRS.h
|
/********************************************************************
DRS.h, S.Ritt, M. Schneebeli - PSI
$Id: DRS.h 13347 2009-04-28 08:24:05Z ritt@PSI.CH $
********************************************************************/
#ifndef DRS_H
#define DRS_H
#include <stdio.h>
#include <string.h>
#ifdef HAVE_LIBUSB
# ifndef HAVE_USB
# define HAVE_USB
# endif
#endif
#ifdef HAVE_USB
# include <musbstd.h>
#endif // HAVE_USB
#ifdef HAVE_VME
# include <mvmestd.h>
#endif // HAVE_VME
/* disable "deprecated" warning */
#ifdef _MSC_VER
#pragma warning(disable: 4996)
#endif
#ifndef NULL
#define NULL 0
#endif
/* transport mode */
#define TR_VME 1
#define TR_USB 2
#define TR_USB2 3
/* address types */
#define T_CTRL 1
#define T_STATUS 2
#define T_RAM 3
#define T_FIFO 4
/* control register bit definitions */
#define BIT_START_TRIG (1<<0) // write a "1" to start domino wave
#define BIT_REINIT_TRIG (1<<1) // write a "1" to stop & reset DRS
#define BIT_SOFT_TRIG (1<<2) // write a "1" to stop and read data to RAM
#define BIT_EEPROM_WRITE_TRIG (1<<3) // write a "1" to write into serial EEPROM
#define BIT_EEPROM_READ_TRIG (1<<4) // write a "1" to read from serial EEPROM
#define BIT_AUTOSTART (1<<16)
#define BIT_DMODE (1<<17) // (*DRS2*) 0: single shot, 1: circular
#define BIT_LED (1<<18) // 1=on, 0=blink during readout
#define BIT_TCAL_EN (1<<19) // switch on (1) / off (0) for 33 MHz calib signal
#define BIT_TCAL_SOURCE (1<<20)
#define BIT_REFCLK_SOURCE (1<<20)
#define BIT_FREQ_AUTO_ADJ (1<<21) // DRS2/3
#define BIT_TRANSP_MODE (1<<21) // DRS4
#define BIT_ENABLE_TRIGGER1 (1<<22) // External LEMO/FP/TRBUS trigger
#define BIT_LONG_START_PULSE (1<<23) // (*DRS2*) 0:short start pulse (>0.8GHz), 1:long start pulse (<0.8GHz)
#define BIT_READOUT_MODE (1<<23) // (*DRS3*) 0:start from first bin, 1:start from domino stop
#define BIT_DELAYED_START (1<<24) // DRS2: start domino wave 400ns after soft trigger, used for waveform
// generator startup
#define BIT_NEG_TRIGGER (1<<24) // DRS4: use high-to-low trigger if set
#define BIT_ACAL_EN (1<<25) // connect DRS to inputs (0) or to DAC6 (1)
#define BIT_TRIGGER_DELAYED (1<<26) // select delayed trigger from trigger bus
#define BIT_DACTIVE (1<<27) // keep domino wave running during readout
#define BIT_STANDBY_MODE (1<<28) // put chip in standby mode
#define BIT_TR_SOURCE1 (1<<29) // trigger source selection bits
#define BIT_TR_SOURCE2 (1<<30) // trigger source selection bits
#define BIT_ENABLE_TRIGGER2 (1<<31) // analog threshold (internal) trigger
/* status register bit definitions */
#define BIT_RUNNING (1<<0) // one if domino wave running or readout in progress
#define BIT_NEW_FREQ1 (1<<1) // one if new frequency measurement available
#define BIT_NEW_FREQ2 (1<<2)
#define BIT_PLL_LOCKED0 (1<<1) // 1 if PLL has locked (DRS4 evaluation board only)
#define BIT_PLL_LOCKED1 (1<<2) // 1 if PLL DRS4 B has locked (DRS4 mezzanine board only)
#define BIT_PLL_LOCKED2 (1<<3) // 1 if PLL DRS4 C has locked (DRS4 mezzanine board only)
#define BIT_PLL_LOCKED3 (1<<4) // 1 if PLL DRS4 D has locked (DRS4 mezzanine board only)
#define BIT_EEPROM_BUSY (1<<5) // 1 if EEPROM operation in progress
/* configuration register bit definitions */
#define BIT_CONFIG_DMODE (1<<8) // 0: single shot, 1: circular
#define BIT_CONFIG_PLLEN (1<<9) // write a "1" to enable the internal PLL
#define BIT_CONFIG_WSRLOOP (1<<10) // write a "1" to connect WSROUT to WSRIN internally
enum DRSBoardConstants {
kNumberOfChannelsV2 = 10,
kNumberOfChannelsV4 = 9,
kNumberOfCalibChannelsV3 = 10,
kNumberOfCalibChannelsV4 = 9,
kNumberOfBins = 1024,
kNumberOfChips = 2,
kFrequencyCacheSize = 10,
kBSplineOrder = 4,
kPreCaliculatedBSplines = 1000,
kPreCaliculatedBSplineGroups = 5,
kNumberOfADCBins = 4096,
kBSplineXMinOffset = 20,
kMaxNumberOfClockCycles = 100,
};
enum DRSErrorCodes {
kSuccess = 0,
kInvalidTriggerSignal = -1,
kWrongChannelOrChip = -2,
kInvalidTransport = -3,
kZeroSuppression = -4,
kWaveNotAvailable = -5
};
/*---- callback class ----*/
class DRSCallback
{
public:
virtual void Progress(int value) = 0;
virtual ~DRSCallback() {};
};
/*------------------------*/
class DRSBoard;
class ResponseCalibration {
protected:
class CalibrationData {
public:
class CalibrationDataChannel {
public:
unsigned char fLimitGroup[kNumberOfBins]; //!
float fMin[kNumberOfBins]; //!
float fRange[kNumberOfBins]; //!
short fOffset[kNumberOfBins]; //!
short fGain[kNumberOfBins]; //!
unsigned short fOffsetADC[kNumberOfBins]; //!
short *fData[kNumberOfBins]; //!
unsigned char *fLookUp[kNumberOfBins]; //!
unsigned short fLookUpOffset[kNumberOfBins]; //!
unsigned char fNumberOfLookUpPoints[kNumberOfBins]; //!
float *fTempData; //!
private:
CalibrationDataChannel(const CalibrationDataChannel &c); // not implemented
CalibrationDataChannel &operator=(const CalibrationDataChannel &rhs); // not implemented
public:
CalibrationDataChannel(int numberOfGridPoints)
:fTempData(new float[numberOfGridPoints]) {
int i;
for (i = 0; i < kNumberOfBins; i++) {
fData[i] = new short[numberOfGridPoints];
}
memset(fLimitGroup, 0, sizeof(fLimitGroup));
memset(fMin, 0, sizeof(fMin));
memset(fRange, 0, sizeof(fRange));
memset(fOffset, 0, sizeof(fOffset));
memset(fGain, 0, sizeof(fGain));
memset(fOffsetADC, 0, sizeof(fOffsetADC));
memset(fLookUp, 0, sizeof(fLookUp));
memset(fLookUpOffset, 0, sizeof(fLookUpOffset));
memset(fNumberOfLookUpPoints, 0, sizeof(fNumberOfLookUpPoints));
}
~CalibrationDataChannel() {
int i;
delete fTempData;
for (i = 0; i < kNumberOfBins; i++) {
delete fData[i];
delete fLookUp[i];
}
}
};
bool fRead; //!
CalibrationDataChannel *fChannel[10]; //!
unsigned char fNumberOfGridPoints; //!
int fHasOffsetCalibration; //!
float fStartTemperature; //!
float fEndTemperature; //!
int *fBSplineOffsetLookUp[kNumberOfADCBins]; //!
float **fBSplineLookUp[kNumberOfADCBins]; //!
float fMin; //!
float fMax; //!
unsigned char fNumberOfLimitGroups; //!
static float fIntRevers[2 * kBSplineOrder - 2];
private:
CalibrationData(const CalibrationData &c); // not implemented
CalibrationData &operator=(const CalibrationData &rhs); // not implemented
public:
CalibrationData(int numberOfGridPoints);
~CalibrationData();
static int CalculateBSpline(int nGrid, float value, float *bsplines);
void PreCalculateBSpline();
void DeletePreCalculatedBSpline();
};
// General Fields
DRSBoard *fBoard;
double fPrecision;
// Fields for creating the Calibration
bool fInitialized;
bool fRecorded;
bool fFitted;
bool fOffset;
bool fCalibrationValid[2];
int fNumberOfPointsLowVolt;
int fNumberOfPoints;
int fNumberOfMode2Bins;
int fNumberOfSamples;
int fNumberOfGridPoints;
int fNumberOfXConstPoints;
int fNumberOfXConstGridPoints;
double fTriggerFrequency;
int fShowStatistics;
FILE *fCalibFile;
int fCurrentLowVoltPoint;
int fCurrentPoint;
int fCurrentSample;
int fCurrentFitChannel;
int fCurrentFitBin;
float *fResponseX[10][kNumberOfBins];
float *fResponseY;
unsigned short **fWaveFormMode3[10];
unsigned short **fWaveFormMode2[10];
unsigned short **fWaveFormOffset[10];
unsigned short **fWaveFormOffsetADC[10];
unsigned short *fSamples;
int *fSampleUsed;
float *fPntX[2];
float *fPntY[2];
float *fUValues[2];
float *fRes[kNumberOfBins];
float *fResX[kNumberOfBins];
double *fXXFit;
double *fYYFit;
double *fWWFit;
double *fYYFitRes;
double *fYYSave;
double *fXXSave;
double fGainMin;
double fGainMax;
float **fStatisticsApprox;
float **fStatisticsApproxExt;
// Fields for applying the Calibration
CalibrationData *fCalibrationData[kNumberOfChips];
private:
ResponseCalibration(const ResponseCalibration &c); // not implemented
ResponseCalibration &operator=(const ResponseCalibration &rhs); // not implemented
public:
ResponseCalibration(DRSBoard* board);
~ResponseCalibration();
void SetCalibrationParameters(int numberOfPointsLowVolt, int numberOfPoints, int numberOfMode2Bins,
int numberOfSamples, int numberOfGridPoints, int numberOfXConstPoints,
int numberOfXConstGridPoints, double triggerFrequency, int showStatistics = 0);
void ResetCalibration();
bool RecordCalibrationPoints(int chipNumber);
bool RecordCalibrationPointsV3(int chipNumber);
bool RecordCalibrationPointsV4(int chipNumber);
bool FitCalibrationPoints(int chipNumber);
bool FitCalibrationPointsV3(int chipNumber);
bool FitCalibrationPointsV4(int chipNumber);
bool OffsetCalibration(int chipNumber);
bool OffsetCalibrationV3(int chipNumber);
bool OffsetCalibrationV4(int chipNumber);
double GetTemperature(unsigned int chipIndex);
bool WriteCalibration(unsigned int chipIndex);
bool WriteCalibrationV3(unsigned int chipIndex);
bool WriteCalibrationV4(unsigned int chipIndex);
bool ReadCalibration(unsigned int chipIndex);
bool ReadCalibrationV3(unsigned int chipIndex);
bool ReadCalibrationV4(unsigned int chipIndex);
bool Calibrate(unsigned int chipIndex, unsigned int channel, float *adcWaveform,
float *uWaveform, float threshold, bool offsetCalib);
bool Calibrate(unsigned int chipIndex, unsigned int channel, unsigned short *adcWaveform, unsigned short *uWaveform,
int triggerCell, float threshold, bool offsetCalib);
bool SubtractADCOffset(unsigned int chipIndex, unsigned int channel, unsigned short *adcWaveform,
unsigned short *adcCalibratedWaveform, unsigned short newBaseLevel);
bool IsRead(int chipIndex) const { return fCalibrationValid[chipIndex]; }
double GetPrecision() const { return fPrecision; };
double GetOffsetAt(int chip,int chn,int bin) const { return fCalibrationData[chip]->fChannel[chn]->fOffset[bin]; };
double GetGainAt(int chip,int chn,int bin) const { return fCalibrationData[chip]->fChannel[chn]->fGain[bin]; };
... 336 more lines ...
|
61
|
Mon Apr 5 17:57:41 2010 |
Heejong Kim | Simple example application to read a DRS evaluation board |
Stefan Ritt wrote: |
Several people asked for s simple application to guide them in writing their own application to read out a DRS board. Such an application has been added in software revions 2.1.1 and is attached to this message. This example program drs_exam.cpp written in C++ does the following necessary steps to access a DRS board:
- Crate a "DRS" object and scan all USB devices
- Display found DRS boards
- Initialize the first found board and set the sampling frequency to 5 GSPS
- Enable internal trigger on channel #1 with 250 mV threshold
- Start acquisition and wait for a trigger
- Read two waveforms (both time and amplitude)
- Repeat this 10 times
I know that we are still missing a good documentation for the DRS API, but I have not yet found the time to do that. I hope the example program is enough for most people to start writing own programs. For Windows users (MS Visual C++ 8.0) there is a drs.sln project file, and for linux users there is a Makefile which can be used to compile this example program.
|
Hi, Stefan,
drs_exam.cpp is working good to read-out one board.
Now I would like to read-out two boards at the same time using the same trigger( external or internal).
I'm trying to understand and modify the original code for control two board.
Meantime, it would be very appreciated if you give any tips for this.
Thanks,
Heejong |
66
|
Tue Apr 13 14:15:16 2010 |
Stefan Ritt | Simple example application to read a DRS evaluation board |
Heejong Kim wrote: |
Stefan Ritt wrote: |
Several people asked for s simple application to guide them in writing their own application to read out a DRS board. Such an application has been added in software revions 2.1.1 and is attached to this message. This example program drs_exam.cpp written in C++ does the following necessary steps to access a DRS board:
- Crate a "DRS" object and scan all USB devices
- Display found DRS boards
- Initialize the first found board and set the sampling frequency to 5 GSPS
- Enable internal trigger on channel #1 with 250 mV threshold
- Start acquisition and wait for a trigger
- Read two waveforms (both time and amplitude)
- Repeat this 10 times
I know that we are still missing a good documentation for the DRS API, but I have not yet found the time to do that. I hope the example program is enough for most people to start writing own programs. For Windows users (MS Visual C++ 8.0) there is a drs.sln project file, and for linux users there is a Makefile which can be used to compile this example program.
|
Hi, Stefan,
drs_exam.cpp is working good to read-out one board.
Now I would like to read-out two boards at the same time using the same trigger( external or internal).
I'm trying to understand and modify the original code for control two board.
Meantime, it would be very appreciated if you give any tips for this.
Thanks,
Heejong
|
The evaluation boards are not really made for multi-board applications. What you have to do is to maintain an external trigger which synchronizes the boards. So you need:
- two boards connected to two USB ports
- an external flip-flop connected to the two trigger inputs of both boards
If a trigger is sent to the flip-flop, it sends a trigger to both evaluation boards. You poll on one of the boards to see if it has triggered (vis IsBusy()), then you read out both boards. Now you have to reset the external flip-flop somehow from the computer. If you have a CAMAC I/O board or some other means of sending a logical signal to it, that could do the job. From the software point, you get a "DRS" object upon initialization, which contains then two "DRSBoard" objects, over which you can iterate. Look at the "drscl" program from the distribution on how to do that. |
613
|
Tue May 30 20:45:30 2017 |
Esperienza Giove | Setting input range | Hello,
is it possible to set a completely negative input range like -1 to 0 or -0.95 to 0.05 ? |
614
|
Tue May 30 21:00:26 2017 |
Stefan Ritt | Setting input range | See elog:531
Esperienza Giove wrote: |
Hello,
is it possible to set a completely negative input range like -1 to 0 or -0.95 to 0.05 ?
|
|
615
|
Tue May 30 21:22:10 2017 |
Esperienza Giove | Setting input range | Thank you
Stefan Ritt wrote: |
See elog:531
Esperienza Giove wrote: |
Hello,
is it possible to set a completely negative input range like -1 to 0 or -0.95 to 0.05 ?
|
|
|
50
|
Tue Mar 9 23:28:45 2010 |
Hao Huan | Serial Interface Frequency of the DRS Chip | Hi Stefan,
in the DRS4 datasheet I read that the optimal frequency for SRCLK is 33MHz. However in the evaluation board firmware SRCLK is toggled at rising edges of the internal 33MHz clock, i.e. the frequency of SRCLK itself is 16.5MHz instead. Is that frequency better than 33MHz?
Thanks!
|
51
|
Wed Mar 10 10:07:28 2010 |
Stefan Ritt | Serial Interface Frequency of the DRS Chip |
Hao Huan wrote: |
in the DRS4 datasheet I read that the optimal frequency for SRCLK is 33MHz. However in the evaluation board firmware SRCLK is toggled at rising edges of the internal 33MHz clock, i.e. the frequency of SRCLK itself is 16.5MHz instead. Is that frequency better than 33MHz?
|
The reason for the 16.5 MHz is the following:
After each block of 32 bins, the DRS4 chip switches an internal segment, which causes some small spike at the analog output of the chip. This spike is a bit wider than 30ns, so if everything is digitized with 33 MHz, then you see small spiked each 32 cells. The appropriate solution would be to modify the firmware to digitize all cells with 30ns (33 MHz) and all cells with the spike with ~50 ns (20 MHz). If you do the ROI readout mode, you don't know for the first 10 cells if one of them belong to this class, since the cell address takes 10 cycles to be read out. So you would first have to read 10 cells, and then if you realize that one of them is one of the problematic ones (cell number modulo 32 is zero), you have to re-read the first 10 cells, and digitize the problematic cell with a longer settling time. Now this is a bit complicated to implement in the firmware, so I was just too lazy to do it and decided to digitize everything with 16.5 MHz. But if you are worried about the dead time, you should consider implementing the mentioned algorithm. |
55
|
Thu Mar 18 21:38:10 2010 |
Hao Huan | Serial Interface Frequency of the DRS Chip |
Stefan Ritt wrote: |
Hao Huan wrote: |
in the DRS4 datasheet I read that the optimal frequency for SRCLK is 33MHz. However in the evaluation board firmware SRCLK is toggled at rising edges of the internal 33MHz clock, i.e. the frequency of SRCLK itself is 16.5MHz instead. Is that frequency better than 33MHz?
|
The reason for the 16.5 MHz is the following:
After each block of 32 bins, the DRS4 chip switches an internal segment, which causes some small spike at the analog output of the chip. This spike is a bit wider than 30ns, so if everything is digitized with 33 MHz, then you see small spiked each 32 cells. The appropriate solution would be to modify the firmware to digitize all cells with 30ns (33 MHz) and all cells with the spike with ~50 ns (20 MHz). If you do the ROI readout mode, you don't know for the first 10 cells if one of them belong to this class, since the cell address takes 10 cycles to be read out. So you would first have to read 10 cells, and then if you realize that one of them is one of the problematic ones (cell number modulo 32 is zero), you have to re-read the first 10 cells, and digitize the problematic cell with a longer settling time. Now this is a bit complicated to implement in the firmware, so I was just too lazy to do it and decided to digitize everything with 16.5 MHz. But if you are worried about the dead time, you should consider implementing the mentioned algorithm.
|
Thanks! The suggested algorithm looks promising. However, if the spikes take place only for those specific cells, is it possible to absorb them into the offset calibration? |
56
|
Thu Mar 18 22:10:41 2010 |
Stefan Ritt | Serial Interface Frequency of the DRS Chip |
Hao Huan wrote: |
Thanks! The suggested algorithm looks promising. However, if the spikes take place only for those specific cells, is it possible to absorb them into the offset calibration?
|
No, since they are not constant. The bus segments charge up between readouts with a time constant of about 0.5s. So if you do the readout with 1Hz event rate and with 100Hz event rate, the peaks will differ by a factor up to 10, so a constant offset correction cannot take care of that. |
866
|
Tue Mar 1 19:03:37 2022 |
Keita Mizukoshi | Scaler issue to evaluate live time | Hi. I'm trying to evaluate livetime of the evaluation board with the hardware scaler. I'm facing a strange issue.
I took the rate with the function, DRS->GetScaler(int channel).
I guess that channels 0--3 mean the rate for the channel, and channel 4 means the counter of the trigger.
I took the 1,000 pulses generated by a pulse generator with 50 Hz.
The scaler values are ~ 39.83, not 50.
The timestamp difference between the initial event and the final event is 19.98 seconds.
1000/19.98 ~ 50, thus, the evaluation board took the pulses with enough livetime.
Can we believe the scaler value for the livetime evaluation? |
869
|
Thu Mar 3 16:14:16 2022 |
Stefan Ritt | Scaler issue to evaluate live time | The scalers are read out 10x per seconds, so they have an accuracy of 10 Hz. I tried a 50 Hz pulser, and measured 40 Hz, I tried 52 Hz and measured 50 Hz. This is about what you can expect.
The scaler rate is measured after the discriminator of the trigger, so the trigger level also affects the scaler reading. If you have a 100 mV pulse and your threshold is 200 mV, your scaler rate drops to zero. That can be seen best with the DRSOsc and sliding the trigger value. If you have a 50 Hz pulse with narrow (< us) pulses, things are fine. But if you use a 50 Hz square wave, then you get distorted signals due to the AC coupling which can also be confusing. See for example here: https://www.daqarta.com/dw_gg0o.htm
Keita Mizukoshi wrote: |
Hi. I'm trying to evaluate livetime of the evaluation board with the hardware scaler. I'm facing a strange issue.
I took the rate with the function, DRS->GetScaler(int channel).
I guess that channels 0--3 mean the rate for the channel, and channel 4 means the counter of the trigger.
I took the 1,000 pulses generated by a pulse generator with 50 Hz.
The scaler values are ~ 39.83, not 50.
The timestamp difference between the initial event and the final event is 19.98 seconds.
1000/19.98 ~ 50, thus, the evaluation board took the pulses with enough livetime.
Can we believe the scaler value for the livetime evaluation?
|
|
870
|
Fri Mar 4 03:55:33 2022 |
Keita Mizukoshi | Scaler issue to evaluate live time | Thank you very much for your explanation.
I would like to show you a pulse example ('black line is the threshold).
Still, pulse generator rate and DRS4 rate are a bit different more than 10 Hz.
Stefan Ritt wrote: |
The scalers are read out 10x per seconds, so they have an accuracy of 10 Hz. I tried a 50 Hz pulser, and measured 40 Hz, I tried 52 Hz and measured 50 Hz. This is about what you can expect.
The scaler rate is measured after the discriminator of the trigger, so the trigger level also affects the scaler reading. If you have a 100 mV pulse and your threshold is 200 mV, your scaler rate drops to zero. That can be seen best with the DRSOsc and sliding the trigger value. If you have a 50 Hz pulse with narrow (< us) pulses, things are fine. But if you use a 50 Hz square wave, then you get distorted signals due to the AC coupling which can also be confusing. See for example here: https://www.daqarta.com/dw_gg0o.htm
Keita Mizukoshi wrote: |
Hi. I'm trying to evaluate livetime of the evaluation board with the hardware scaler. I'm facing a strange issue.
I took the rate with the function, DRS->GetScaler(int channel).
I guess that channels 0--3 mean the rate for the channel, and channel 4 means the counter of the trigger.
I took the 1,000 pulses generated by a pulse generator with 50 Hz.
The scaler values are ~ 39.83, not 50.
The timestamp difference between the initial event and the final event is 19.98 seconds.
1000/19.98 ~ 50, thus, the evaluation board took the pulses with enough livetime.
Can we believe the scaler value for the livetime evaluation?
|
|
|
Attachment 1: pulse_example.png
|
|
Attachment 2: rate.png
|
|
874
|
Mon Mar 7 16:37:54 2022 |
Stefan Ritt | Scaler issue to evaluate live time | I tried your measurement with the DRSOscilloscope app (see below), and I measure a constant difference of 10 Hz among the whole range of 100 Hz to 3 kHz.
So I don't know what's wrong on your side. Did you try with the oscilloscope app as well? Have you checked your pulse generator? The evaluation board time reference is a quartz with an accuracy of 10^-5, so no way one can get there a difference you see.
Stefan
Keita Mizukoshi wrote: |
Thank you very much for your explanation.
I would like to show you a pulse example ('black line is the threshold).
Still, pulse generator rate and DRS4 rate are a bit different more than 10 Hz.
Stefan Ritt wrote: |
The scalers are read out 10x per seconds, so they have an accuracy of 10 Hz. I tried a 50 Hz pulser, and measured 40 Hz, I tried 52 Hz and measured 50 Hz. This is about what you can expect.
The scaler rate is measured after the discriminator of the trigger, so the trigger level also affects the scaler reading. If you have a 100 mV pulse and your threshold is 200 mV, your scaler rate drops to zero. That can be seen best with the DRSOsc and sliding the trigger value. If you have a 50 Hz pulse with narrow (< us) pulses, things are fine. But if you use a 50 Hz square wave, then you get distorted signals due to the AC coupling which can also be confusing. See for example here: https://www.daqarta.com/dw_gg0o.htm
Keita Mizukoshi wrote: |
Hi. I'm trying to evaluate livetime of the evaluation board with the hardware scaler. I'm facing a strange issue.
I took the rate with the function, DRS->GetScaler(int channel).
I guess that channels 0--3 mean the rate for the channel, and channel 4 means the counter of the trigger.
I took the 1,000 pulses generated by a pulse generator with 50 Hz.
The scaler values are ~ 39.83, not 50.
The timestamp difference between the initial event and the final event is 19.98 seconds.
1000/19.98 ~ 50, thus, the evaluation board took the pulses with enough livetime.
Can we believe the scaler value for the livetime evaluation?
|
|
|
|
Attachment 1: Screenshot_2022-03-07_at_16.37.32_.png
|
|
Attachment 2: Screenshot_2022-03-07_at_16.35.44_.png
|
|
773
|
Fri Sep 13 15:27:41 2019 |
Arseny Rybnikov | Scaler / How to modify the firmware to change the scaler integration time | Hello,
We want to use the inner DRS4 counter(scaler) within more than the 100ms integration time. We guess that we need to modify the original firmware around this point:
-- Reference clock used for frequency counter
proc_1hzclk: process(I_RESET, I_CLK33)
begin
if (I_RESET = '1') then
drs_1hz_counter(31 downto 0) <= (others => '0');
drs_1hz_clock <= '0';
scaler_reset <= (others => '1');
scaler_ff_reset <= (others => '1');
elsif rising_edge(I_CLK33) then
drs_1hz_counter <= drs_1hz_counter - 1; -- count down
scaler_reset <= (others => '0');
scaler_ff_reset <= (others => '0');
-- toggle refclk if timer expires
if (drs_1hz_counter(drs_1hz_counter'high) = '1') then
drs_1hz_clock <= not drs_1hz_clock;
drs_1hz_counter(31 downto 0) <= X"0016E35F"; -- 1499999, I_CLK33 is actually a 30 MHz clock
scaler_ff_reset <= (others => '1'); -- reset scaler_ff once every 100ms cycle
loop_scaler_reset : for i in 0 to 5 loop
if (scaler_ff(i) = '0') then -- no activity since last cycle?
scaler_reset(i) <= '1'; -- force clear scaler register
end if;
end loop;
if (scaler_ff(0) = '0') then -- no activity since last cycle?
scaler_reset(0) <= '1'; -- force clear scaler register
end if;
end if;
end if;
end process;
Could you please tell us how to modify the firmware to increse the time up to 5 seconds for instance?
Thanks in advance, Arseny |
454
|
Thu Nov 26 18:59:27 2015 |
Robert Adams | Saving histogram data | I would really love to be able to save histogram data, though I have not been able to do this. I could take a screenshot and extract the data from an image, but would prefer to avoid this if there is a simpler way... possibly I have overlooked something obvious? Thanks very much for any advice or tips. |
477
|
Tue Feb 16 11:21:43 2016 |
Stefan Ritt | Saving histogram data | There is no histogram save functoinality in ther DRSOscilloscope program - on purpose. The board and the software are meant to evaluate the board, not to replace a full DAQ system. If we want to save histograms, you maybe also want to set the range, make cuts, do fits etc. So it would take lots of resources to add all that. Therefore we recommend to use the stand-alone C program drs_exam.cpp to read the board, the you can either do whatever you want in the C program, including saving of histograms. Alternatively, you can use ROOT to analyze binary stored DRS data and do whatever histogram manipulation you want there.
Stefan
Robert Adams wrote: |
I would really love to be able to save histogram data, though I have not been able to do this. I could take a screenshot and extract the data from an image, but would prefer to avoid this if there is a simpler way... possibly I have overlooked something obvious? Thanks very much for any advice or tips.
|
|
478
|
Tue Feb 16 11:55:54 2016 |
Martin Petriska | Saving histogram data |
Robert Adams wrote: |
I would really love to be able to save histogram data, though I have not been able to do this. I could take a screenshot and extract the data from an image, but would prefer to avoid this if there is a simpler way... possibly I have overlooked something obvious? Thanks very much for any advice or tips.
|
You can use qtpals, there is posibility to save histograms (energy, time diference), only set trigger on channel which you use. https://sourceforge.net/projects/qtpals/files/?source=navbar |
|