Commit cc9cdb55 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

software: added lib & test program

parent 5598a946
*.o
fd_test
\ No newline at end of file
CFLAGS = -I/home/slayer/rr/gnurabbit/kernel -Iinclude -g
OBJS = fdelay_lib.o fdelay_test.o rr_io.o
all: $(OBJS)
gcc -o fd_test $(OBJS) -lm
clean:
rm -f fd_test $(OBJS)
\ No newline at end of file
/*
FmcDelay1ns4Cha (a.k.a. The Fine Delay Card)
User-space driver/library
Tomasz Włostowski/BE-CO-HT, 2011
(c) Copyright CERN 2011
Licensed under LGPL 2.1
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/time.h>
#include <math.h>
#include "rr_io.h"
#include "fdelay_regs.h"
#include "pll_config.h"
#include "acam_gpx.h"
#include "fdelay_lib.h"
/* SPI Bus chip selects */
#define CS_PLL 1 /* AD9516 PLL */
#define CS_GPIO 2 /* MCP23S17 GPIO */
/* MCP23S17 GPIO expander pin locations: bit 8 = select bank 2, bits 7..0 = mask of the pin in the selected bank */
#define SGPIO_TERM_EN (0x100 | (1<<7)) /* Input termination enable (1 = on) */
#define SGPIO_LED_TERM (0x100 | (1<<2)) /* Termination enable LED (1 = on) */
#define SGPIO_DRV_OEN (0x100 | (1<<0)) /* Output driver enable (0 = on) */
#define SGPIO_TRIG_SEL (0x100 | (1<<3)) /* TDC trigger select (0 = trigger input, 1 = FPGA) */
/* ACAM TDC operation modes */
#define ACAM_RMODE 0
#define ACAM_IMODE 1
/* MCP23S17 register addresses (only ones which are used by the lib) */
#define MCP_IODIR 0x0
#define MCP_GPIO 0x12
#define MCP_IOCON 0x0a
/* Number of fractional bits in the timestamps/time definitions. Must be consistent with the HDL bitstream. */
#define FDELAY_FRAC_BITS 12
/* Fractional bits shifted away when converting the fine (< 8ns) part to fit the range of SY89295 delay line. */
#define FDELAY_SCALER_SHIFT 12
/* Number of delay line taps */
#define FDELAY_NUM_TAPS 1024
/* How many times each calibration measurement will be averaged */
#define FDELAY_CAL_AVG_STEPS 1024
/* Fine Delay Card Magic ID */
#define FDELAY_MAGIC_ID 0xf19ede1a
/* RSTR Register value which triggers a reset of the FD Core */
#define FDELAY_RSTR_TRIGGER 0xdeadbeef
/* ACAM Calibration parameters */
struct fine_delay_calibration {
uint32_t magic; /* magic ID: 0xf19ede1a */
uint32_t zero_offset[4]; /* Output zero offset, in nsec << FDELAY_FRAC_BITS */
uint32_t adsfr_val; /* ADSFR register value */
uint32_t acam_start_offset; /* ACAM Start offset value */
uint32_t atmcr_val; /* ATMCR register value */
int32_t dly_tempco[4]; /* SY89295 delay/temperature coefficient in ps/degC << FDELAY_FRAC_BITS */
int32_t zero_tempco[4]; /* Zero offset/temperature coefficient in ps/degC << FDELAY_FRAC_BITS */
int32_t cal_temp; /* Calibration temperature in 0.1 degC */
} __attribute__((packed));
/* Internal state of the fine delay card */
struct fine_delay_hw
{
uint32_t base_addr; /* Base address of the core */
uint32_t base_spi; /* SPI Controller offset */
double acam_bin; /* bin size of the ACAM TDC - calculated for */
uint32_t frr[4]; /* Fine range register for each output, determi*/
int32_t board_temp; /* Current temperature of the board in 0.1 degC */
struct fine_delay_calibration calib;
};
/*
----------------------
Some utility functions
----------------------
*/
static int extra_debug = 1;
static void dbg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if(extra_debug)
vfprintf(stderr,fmt,ap);
va_end(ap);
}
/* Returns the numer of microsecond timer ticks */
static inline int64_t get_tics()
{
struct timezone tz= {0,0};
struct timeval tv;
gettimeofday(&tv, &tz);
return (int64_t)tv.tv_sec * 1000000LL + (int64_t) tv.tv_usec;
}
/* Microsecond-accurate delay */
static void udelay(uint32_t usecs)
{
int64_t ts = get_tics();
while(get_tics() - ts < (int64_t)usecs);
}
/* useful declaration/wrapper macros */
#define fd_writel(data, addr) dev->writel(dev->priv_io, data, (dev->base_addr + (addr)))
#define fd_readl(addr) dev->readl(dev->priv_io, (dev->base_addr + (addr)))
#define fd_decl_private(dev) struct fine_delay_hw *hw = (struct fine_delay_hw *) dev->priv_fd;
/*
----------------------------------
Simple SPI Master driver
----------------------------------
*/
/* Initializes the SPI Controller */
static void oc_spi_init(fdelay_device_t *dev)
{
fd_decl_private(dev)
}
/* Sends (num_bits) from (in) to slave at CS line (ss), storint the readback data in (*out) */
static void oc_spi_txrx(fdelay_device_t *dev, int ss, int num_bits, uint32_t in, uint32_t *out)
{
fd_decl_private(dev);
uint32_t scr;
scr = FD_SCR_DATA_W(in) | FD_SCR_CPOL;
if(ss == CS_PLL)
scr |= FD_SCR_SEL_PLL;
else if(ss == CS_GPIO)
scr |= FD_SCR_SEL_GPIO;
fd_writel(scr, FD_REG_SCR);
fd_writel(scr | FD_SCR_START, FD_REG_SCR);
while(! (fd_readl(FD_REG_SCR) & FD_SCR_READY))
scr = fd_readl(FD_REG_SCR);
if(out) *out = FD_SCR_DATA_R(scr);
udelay(100);
}
/*
-----------------
AD9516 PLL Driver
-----------------
*/
/* Writes an AD9516 register */
static inline void ad9516_write_reg(fdelay_device_t *dev, uint16_t reg, uint8_t val)
{
oc_spi_txrx(dev, CS_PLL, 24, ((uint32_t)(reg & 0xfff) << 8) | val, NULL);
}
/* Reads a register from AD9516 */
static inline uint8_t ad9516_read_reg(fdelay_device_t *dev, uint16_t reg)
{
uint32_t rval;
oc_spi_txrx(dev, CS_PLL, 24, ((uint32_t)(reg & 0xfff) << 8) | (1<<23), &rval);
return rval & 0xff;
}
/* Initializes the AD9516 PLL by loading a pre-defined register set and waiting until the PLL has locked */
static int ad9516_init(fdelay_device_t *dev)
{
fd_decl_private(dev)
int i;
const int64_t lock_timeout = 10000000LL;
int64_t start_tics;
dbg("%s: Initializing AD9516 PLL...\n", __FUNCTION__);
ad9516_write_reg(dev, 0, 0x99);
ad9516_write_reg(dev, 0x232, 1);
/* Check if the chip is present by reading its ID register */
if(ad9516_read_reg(dev, 0x3) != 0xc3)
{
dbg("%s: AD9516 PLL not responding.\n", __FUNCTION__);
return -1;
}
/* Load the regs */
for(i=0;ad9516_regs[i].reg >=0 ;i++)
ad9516_write_reg (dev, ad9516_regs[i].reg, ad9516_regs[i].val);
/* Wait until the PLL has locked */
start_tics = get_tics();
for(;;)
{
if(ad9516_read_reg(dev, 0x1f) & 1)
break;
if(get_tics() - start_tics > lock_timeout)
{
dbg("%s: AD9516 PLL does not lock.\n", __FUNCTION__);
return -1;
}
udelay(100);
}
/* Synchronize the phase of all clock outputs (this is critical for the accuracy!) */
ad9516_write_reg(dev, 0x230, 1);
ad9516_write_reg(dev, 0x232, 1);
ad9516_write_reg(dev, 0x230, 0);
ad9516_write_reg(dev, 0x232, 1);
dbg("%s: AD9516 locked.\n", __FUNCTION__);
return 0;
}
/*
----------------------------
MCP23S17 SPI I/O Port Driver
----------------------------
*/
/* Writes MCP23S17 register */
static inline void mcp_write(fdelay_device_t *dev, uint8_t reg, uint8_t val)
{
oc_spi_txrx(dev, CS_GPIO, 24, 0x4e0000 | ((uint32_t)reg<<8) | val, NULL);
}
/* Reads MCP23S17 register */
static uint8_t mcp_read(fdelay_device_t *dev, uint8_t reg)
{
uint32_t rval;
oc_spi_txrx(dev, CS_GPIO, 24, 0x4f0000 | ((uint32_t)reg<<8), &rval);
return rval & 0xff;
}
/* Sets the direction (0 = input, non-zero = output) of a particular MCP23S17 GPIO pin */
static void sgpio_set_dir(fdelay_device_t *dev, int pin, int dir)
{
uint8_t iodir = (MCP_IODIR) + (pin & 0x100 ? 1 : 0);
uint8_t x;
x = mcp_read(dev, iodir);
if(dir) x &= ~(pin); else x |= (pin);
mcp_write(dev, iodir, x);
}
/* Sets the value on a given MCP23S17 GPIO pin */
static void sgpio_set_pin(fdelay_device_t *dev, int pin, int val)
{
uint8_t gpio = (MCP_GPIO) + (pin & 0x100 ? 1 : 0);
uint8_t x;
x = mcp_read(dev, gpio);
if(!val) x &= ~(pin); else x |= (pin);
mcp_write(dev, gpio, x);
}
/*
----------------------------------------
ACAM Time To Digital Converter functions
----------------------------------------
*/
/* Writes a particular ACAM register. Works only if (GCR.BYPASS == 1) - i.e. when
the ACAM is controlled from the host instead of the delay core. */
static void acam_write_reg(fdelay_device_t *dev, uint8_t reg, uint32_t data)
{
fd_decl_private(dev)
fd_writel((((uint32_t) (reg)) << 28) | (data & 0xfffffff), FD_REG_TAR);
udelay(1);
fd_writel(FD_TDCSR_WRITE, FD_REG_TDCSR);
udelay(1);
}
/* Reads a register from the ACAM TDC. As for the function above, GCR.BYPASS must be enabled */
static uint32_t acam_read_reg(fdelay_device_t *dev, uint8_t reg)
{
fd_decl_private(dev)
fd_writel((((uint32_t) (reg)) << 28), FD_REG_TAR);
udelay(1);
fd_writel(FD_TDCSR_READ, FD_REG_TDCSR);
udelay(1);
return fd_readl(FD_REG_TAR) & 0xfffffff;
}
/* Calculates the parameters of the ACAM PLL (hsdiv and refdiv)
for a given bin size and reference clock frequency. Returns the closest
achievable bin size. */
static double acam_calc_pll(int *hsdiv, int *refdiv, double bin, double clock_freq)
{
int h;
int r;
double best_err = 100000;
double best_bin;
/* Try all possible divider settings */
for(h=1;h<=255;h++)
for(r=0;r<=7;r++)
{
double b = ((1.0/clock_freq) * 1e12) * pow(2.0, (double) r) / (216.0 * (double)h);
if(fabs(bin - b) < best_err)
{
best_err=fabs(bin-b);
best_bin = b;
*hsdiv= h;
*refdiv = r;
}
}
dbg("%s: requested bin=%.02fps best=%.02fps error=%.02f%%\n", __FUNCTION__, bin, best_bin, (best_err/bin) * 100.0);
dbg("%s: hsdiv=%d refdiv=%d\n", __FUNCTION__, *hsdiv, *refdiv);
return best_bin;
}
/* Returns non-zero if the ACAM's internal PLL is locked */
static inline int acam_pll_locked(fdelay_device_t *dev)
{
uint32_t r12 = acam_read_reg(dev, 12);
return !(r12 & AR12_NotLocked);
}
/* Configures the ACAM TDC to work in a particular mode. Currently there are two modes
supported: R-Mode for the normal operation (delay/timestamper) and I-Mode for the purpose
of calibrating the fine delay lines. */
static int acam_configure(fdelay_device_t *dev, int mode)
{
fd_decl_private(dev)
int hsdiv, refdiv;
int64_t start_tics;
const int64_t lock_timeout = 2000000LL;
hw->acam_bin = acam_calc_pll(&hsdiv, &refdiv, 80.9553, 31.25e6) / 3.0;
/* Disable TDC inputs prior to configuring */
fd_writel(FD_TDCSR_STOP_DIS | FD_TDCSR_START_DIS, FD_REG_TDCSR);
if(mode == ACAM_RMODE)
{
acam_write_reg(dev, 0, AR0_ROsc | AR0_RiseEn0 | AR0_RiseEn1 | AR0_HQSel );
acam_write_reg(dev, 1, AR1_Adj(0, 0) |
AR1_Adj(1, 2) |
AR1_Adj(2, 6) |
AR1_Adj(3, 0) |
AR1_Adj(4, 2) |
AR1_Adj(5, 6) |
AR1_Adj(6, 0));
acam_write_reg(dev, 2, AR2_RMode | AR2_Adj(7, 2) | AR2_Adj(8, 6));
acam_write_reg(dev, 3, 0);
acam_write_reg(dev, 4, AR4_EFlagHiZN);
acam_write_reg(dev, 5, AR5_StartRetrig |AR5_StartOff1(hw->calib.acam_start_offset) | AR5_MasterAluTrig);
acam_write_reg(dev, 6, AR6_Fill(200) | AR6_PowerOnECL);
acam_write_reg(dev, 7, AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv) | AR7_ResAdj | AR7_NegPhase);
acam_write_reg(dev, 11, 0x7ff0000);
acam_write_reg(dev, 12, 0x0000000);
acam_write_reg(dev, 14, 0);
/* Reset the ACAM after the configuration */
acam_write_reg(dev, 4, AR4_EFlagHiZN | AR4_MasterReset | AR4_StartTimer(0));
} else if (mode == ACAM_IMODE)
{
acam_write_reg(dev, 0, AR0_TRiseEn(0) | AR0_HQSel | AR0_ROsc);
acam_write_reg(dev, 2, AR2_IMode);
acam_write_reg(dev, 5, AR5_StartOff1(3000) | AR5_MasterAluTrig);
acam_write_reg(dev, 6, 0);
acam_write_reg(dev, 7, AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv) | AR7_ResAdj | AR7_NegPhase);
acam_write_reg(dev, 11, 0x7ff0000);
acam_write_reg(dev, 12, 0x0000000);
acam_write_reg(dev, 14, 0);
/* Reset the ACAM after the configuration */
acam_write_reg(dev, 4, AR4_EFlagHiZN | AR4_MasterReset | AR4_StartTimer(0));
} else
return -1; /* Unsupported mode? */
dbg("%s: Waiting for ACAM ring oscillator lock...\n", __FUNCTION__);
start_tics = get_tics();
for(;;)
{
if(acam_pll_locked(dev))
break;
if(get_tics() - start_tics > lock_timeout)
{
dbg("%s: ACAM PLL does not lock.\n", __FUNCTION__);
return -1;
}
usleep(10000);
}
return 0;
}
/*
---------------------
Calibration functions
---------------------
*/
/* Measures the the FPGA-generated TDC start and the output of one of the fine delay chips (channel)
at a pre-defined number of taps (fine). Retuns the delay in picoseconds. The measurement is repeated
and averaged (n_avgs) times. Also, the standard deviation of the result can be written to (sdev)
if it's not NULL. */
static double measure_output_delay(fdelay_device_t *dev, int channel, int fine, int n_avgs, double *sdev)
{
fd_decl_private(dev)
double acc = 0.0, std = 0.0;
int i;
/* Mapping between the channel of the delay card and the stop inputs of the ACAM */
int chan_to_acam[5] = {0, 5, 6, 3, 4};
/* Mapping between the channel number and the time tag FIFOs of the ACAM */
int chan_to_fifo[5] = {0, 9, 9, 8, 8};
double rec[1024];
/* Enable the stop input in the ACAM corresponding to the channel being calibrated */
acam_write_reg(dev, 0, AR0_TRiseEn(0) | AR0_TRiseEn(chan_to_acam[channel]) | AR0_HQSel | AR0_ROsc);
/* Program the output delay line setpoint */
fd_writel( fine, FD_REG_FRR1 + 0x20 * (channel - 1));
fd_writel( FD_DCR1_FORCE_DLY | FD_DCR1_POL, FD_REG_DCR1 + 0x20 * (channel - 1));
/* Set the calibration pulse mask to genrate calibration pulses only on one channel at a time.
This minimizes the crosstalk in the output buffer which can severely decrease the accuracy
of calibration measurements */
fd_writel( FD_CALR_PSEL_W(1<<(channel-1)), FD_REG_CALR);
udelay(1);
/* Do n_avgs single measurements and average */
for(i=0;i<n_avgs;i++)
{
uint32_t fr;
/* Re-arm the ACAM (it's working in a single-shot mode) */
fd_writel( FD_TDCSR_ALUTRIG, FD_REG_TDCSR);
udelay(1);
/* Produce a calibration pulse on the TDC start and the appropriate output channel */
fd_writel( FD_CALR_CAL_PULSE | FD_CALR_PSEL_W((1<<(channel-1))), FD_REG_CALR);
udelay(1);
/* read the tag, convert to picoseconds and average */
fr = acam_read_reg(dev, chan_to_fifo[channel]);
double tag = (double)((fr >> 0) & 0x1ffff) * hw->acam_bin * 3.0;
acc += tag;
rec[i] = tag;
}
/* Calculate standard dev and average value */
acc /= (double) n_avgs;
for(i=0;i<n_avgs;i++)
std += (rec[i] - acc) * (rec[i] - acc);
if(sdev) *sdev = sqrt(std /(double) n_avgs);
return acc;
}
/* Measures the transfer function of the fine delay line. Used for testing/debugging purposes. */
static void dbg_transfer_function(fdelay_device_t *dev)
{
fd_decl_private(dev)
int channel, i;
double bias, x, meas[FDELAY_NUM_TAPS][4], sdev[FDELAY_NUM_TAPS][4];
fd_writel( FD_GCR_BYPASS, FD_REG_GCR);
acam_configure(dev, ACAM_IMODE);
fd_writel( FD_TDCSR_START_EN | FD_TDCSR_STOP_EN, FD_REG_TDCSR);
for(channel = 1; channel <= 4; channel++)
{
dbg("calibrating channel %d\n", channel);
bias = measure_output_delay(dev, channel, 0, FDELAY_CAL_AVG_STEPS, &sdev[0][channel-1]);
meas[0][channel-1] = 0.0;
for(i=FDELAY_NUM_TAPS-1;i>=0;i--)
{
x = measure_output_delay(dev, channel, i,
FDELAY_CAL_AVG_STEPS, &sdev[i][channel-1]);
meas[i][channel-1] = x - bias;
}
}
FILE *f=fopen("t_func.dat","w");
for(i=0;i<FDELAY_NUM_TAPS;i++)
{
fprintf(f, "%d %.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f\n", i,
meas[i][0], meas[i][1], meas[i][2], meas[i][3],
sdev[i][0], sdev[i][1], sdev[i][2], sdev[i][3]);
}
fclose(f);
}
/* Finds the preset (i.e. the numer of taps) of the output delay line in (channel)
at which it introduces exactly 8 ns more than when it's programmed to 0 taps.
Uses a binary search algorithm to speed up the calibration (assuming that the
line is monotonous). */
static int find_8ns_tap(fdelay_device_t *dev, int channel)
{
int l = 0, r=FDELAY_NUM_TAPS-1;
/* Measure the delay at zero setting, so it can be further subtracted to get only the
delay part introduced by the delay line (ingoring the TDC, FPGA and routing delays). */
double bias = measure_output_delay(dev, channel, 0, FDELAY_CAL_AVG_STEPS, NULL);
while(abs(l-r)>1)
{
int mid = (l+r) / 2;
double dly = measure_output_delay(dev, channel, mid, FDELAY_CAL_AVG_STEPS, NULL) - bias;
if(dly < 8000.0) l = mid; else r = mid;
}
return l;
}
/* Performs the startup calibration of the output delay lines. */
void calibrate_outputs(fdelay_device_t *dev)
{
fd_decl_private(dev)
int i, channel;
// dbg_transfer_function(dev);
fd_writel( FD_GCR_BYPASS, FD_REG_GCR);
acam_configure(dev, ACAM_IMODE);
fd_writel( FD_TDCSR_START_EN | FD_TDCSR_STOP_EN, FD_REG_TDCSR);
for(channel = 1; channel <= 4; channel++)
{
int cal_val = find_8ns_tap(dev, channel);
dbg("%s: CH%d: 8ns @ %d\n", __FUNCTION__, channel, cal_val);
hw->frr[channel-1] = cal_val;
}
}
#if 0
void poll_stats()
{
int raw = fd_readl(FD_REG_IECRAW);
int tagged = fd_readl(FD_REG_IECTAG);
int pd = fd_readl(FD_REG_IEPD) & 0xff;
if(events_raw != raw || events_tagged != tagged || pd != tag_delay)
{
events_raw = raw;
events_tagged = tagged;
tag_delay = pd;
// if(events_raw != events_tagged) printf("ERROR: raw %d vs tagged %d\n", raw,tagged);
// printf("NewStats: raw %d tagged %d pdelay %d nsec\n", raw, tagged ,(pd+3)*8);
}
}
#endif
/*
-------------------------------------
Public API
-------------------------------------
*/
/* Initialize & self-calibrate the Fine Delay card */
int fdelay_init(fdelay_device_t *dev)
{
struct fine_delay_hw *hw;
hw = (struct fine_delay_hw *) malloc(sizeof(struct fine_delay_hw));
if(! hw)
return -1;
dev->priv_fd = (void *) hw;
hw->base_addr = dev->base_addr;
hw->base_spi = 0x100;
/* Fixme: read these from the calibration EEPROM */
hw->calib.atmcr_val = 1 | (2000 << 4);
hw->calib.adsfr_val = 56648;
hw->calib.acam_start_offset = 10000;
dbg("%s: Initializing the Fine Delay Card\n", __FUNCTION__);
/* Read the Identification register and check if we are talking to a proper Fine Delay HDL Core */
if(fd_readl(FD_REG_IDR) != FDELAY_MAGIC_ID)
{
dbg("%s: invalid core signature. Are you sure you have loaded the FPGA with the Fine Delay firmware?\n", __FUNCTION__);
return -1;
}
/* Initialize the clock system - AD9516 PLL */
oc_spi_init(dev);
if(ad9516_init(dev) < 0)
return -1;
/* Configure default states of the SPI GPIO pins */
sgpio_set_dir(dev, SGPIO_LED_TERM, 1);
sgpio_set_pin(dev, SGPIO_LED_TERM, 0);
sgpio_set_dir(dev, SGPIO_TRIG_SEL, 1);
sgpio_set_pin(dev, SGPIO_TRIG_SEL, 1);
sgpio_set_dir(dev, SGPIO_DRV_OEN, 1);
sgpio_set_pin(dev, SGPIO_DRV_OEN, 1);
sgpio_set_dir(dev, SGPIO_TERM_EN, 1);
sgpio_set_pin(dev, SGPIO_TERM_EN, 0);
/* Reset the FD core once we have proper reference/TDC clocks */
fd_writel( 0xdeadbeef, FD_REG_RSTR);
/* Disable the delay generator core, so we can access the ACAM from the host, both for
initialization and calibration */
fd_writel( FD_GCR_BYPASS, FD_REG_GCR);
/* Calibrate the output delay lines */
calibrate_outputs(dev);
/* Switch to the R-MODE (more precise) */
acam_configure(dev, ACAM_RMODE);
/* Switch the ACAM to be driven by the delay core instead of the host */
fd_writel( 0, FD_REG_GCR);
/* Clear and disable the timestamp readout buffer */
fd_writel( FD_TSBCR_PURGE | FD_TSBCR_RST_SEQ, FD_REG_TSBCR);
/* Program the ACAM-specific timestamper registers using pre-defined calibration values:
- bin -> internal timebase scalefactor (ADSFR),
- Start offset (must be consistent with the value written to the ACAM reg 4)
- timestamp merging control register (ATMCR) */
fd_writel( hw->calib.adsfr_val, FD_REG_ADSFR);
fd_writel( 3 * hw->calib.acam_start_offset, FD_REG_ASOR);
fd_writel( hw->calib.atmcr_val, FD_REG_ATMCR);
/* Synchronize the internal time base - for the time being to itself (i.e. ensure that
all the time counters inside the FD Core are in sync */
fd_writel(FD_GCR_CSYNC_INT, FD_REG_GCR);
/* Enable outputs */
sgpio_set_pin(dev, SGPIO_DRV_OEN, 1);
dbg("FD initialized\n");
return 0;
}
/* Configures the trigger input. Enable enables the input, termination selects the impedance
of the trigger input (0 == 2kohm, 1 = 50 ohm) */
int fdelay_configure_trigger(fdelay_device_t *dev, int enable, int termination)
{
if(termination)
{
dbg("%s: 50-ohm terminated mode\n", __FUNCTION__);
sgpio_set_pin(dev,SGPIO_LED_TERM,1);
sgpio_set_pin(dev,SGPIO_TERM_EN,1);
} else {
dbg("%s: high impedance mode\n", __FUNCTION__);
sgpio_set_pin(dev,SGPIO_LED_TERM,0);
sgpio_set_pin(dev,SGPIO_TERM_EN,0);
};
if(enable)
fd_writel(FD_GCR_INPUT_EN, FD_REG_GCR);
else
fd_writel(0, FD_REG_GCR);
return 0;
}
/* Converts a positive time interval expressed in picoseconds to the timestamp format used in the Fine Delay core */
fdelay_time_t fdelay_from_picos(const uint64_t ps)
{
fdelay_time_t t;
int64_t rescaled;
rescaled = (int64_t) ((long double) ps * (long double)4096 / (long double)8000);
t.frac = rescaled % 4096;
rescaled -= t.frac;
rescaled /= 4096;
t.coarse = rescaled % 125000000;
rescaled -= t.coarse;
rescaled /= 125000000;
t.utc = rescaled;
//dbg("fdelay_from_picos: %d:%d:%d\n", t.utc, t.coarse, t.frac);
return t;
}
/* Converts a Fine Delay time stamp to plain picoseconds */
int64_t fdelay_to_picos(const fdelay_time_t t)
{
int64_t tp = (((int64_t)t.frac * 8000LL) >> 12) + ((int64_t) t.coarse * 8000LL) + ((int64_t)t.utc * 1000000000000LL);
return tp;
}
static int poll_rbuf(fdelay_device_t *dev)
{
if((fd_readl(FD_REG_TSBCR) & FD_TSBCR_EMPTY) == 0)
return 1;
return 0;
}
/* Reads up to (how_many) timestamps from the FD ring buffer and stores them in (timestamps).
Returns the number of read timestamps. */
int fdelay_read(fdelay_device_t *dev, fdelay_time_t *timestamps, int how_many)
{
int n_read = 0;
while(poll_rbuf(dev))
{
fdelay_time_t ts;
uint32_t seq_frac;
if(!how_many) break;
ts.utc = fd_readl(FD_REG_TSBR_U);
ts.coarse = fd_readl(FD_REG_TSBR_C) & 0xfffffff;
seq_frac = fd_readl(FD_REG_TSBR_FID);
ts.frac = seq_frac & 0xfff;
ts.seq_id = seq_frac >> 16;
*timestamps++ = ts;
how_many--;
n_read++;
}
return n_read;
}
/* Configures the output channel (channel) to produce pulses delayed from the trigger by (delay_ps).
The output pulse width is proviced in (width_ps) parameter. */
int fdelay_configure_output(fdelay_device_t *dev, int channel, int enable, int64_t delay_ps, int64_t width_ps)
{
fd_decl_private(dev)
uint32_t base = (channel-1) * 0x20;
uint32_t dcr;
fdelay_time_t start, end;
if(channel < 1 || channel > 4)
return -1;
start = fdelay_from_picos(delay_ps);
end = fdelay_from_picos(delay_ps + width_ps);
fd_writel(hw->frr[channel-1], base + FD_REG_FRR1);
fd_writel(start.utc, base + FD_REG_U_START1);
fd_writel(start.coarse, base + FD_REG_C_START1);
fd_writel(start.frac, base + FD_REG_F_START1);
fd_writel(end.utc, base + FD_REG_U_END1);
fd_writel(end.coarse, base + FD_REG_C_END1);
fd_writel(end.frac, base + FD_REG_F_END1);
dcr = (enable ? FD_DCR1_ENABLE : 0)
| FD_DCR1_POL
| FD_DCR1_UPDATE;
fd_writel(dcr, base + FD_REG_DCR1);
return 0;
}
#if 0
int fdelay_get_raw(int *coarse, int *frac)
{
if(! (fd_readl(FD_REG_RAWFIFO_CSR) & FD_RAWFIFO_CSR_EMPTY))
{
*frac = fd_readl(FD_REG_RAWFIFO_R0) & 0xfffff;
*coarse = fd_readl(FD_REG_RAWFIFO_R1) & 0xfffffff;
return 1;
}
return 0;
}
#endif
#include <stdio.h>
#include "fdelay_lib.h"
#include "rr_io.h"
void my_writel(void *priv, uint32_t data, uint32_t addr)
{
rr_writel(data, addr);
}
uint32_t my_readl(void *priv, uint32_t addr)
{
uint32_t d = rr_readl(addr);
return d;
}
main()
{
fdelay_device_t dev;
rr_init();
dev.writel = my_writel;
dev.readl = my_readl;
dev.base_addr = 0x80400;
if(fdelay_init(&dev) < 0)
return -1;
fdelay_configure_trigger(&dev, 1,1);
fdelay_configure_output(&dev,1,1,500000, 200000);
fdelay_configure_output(&dev,2,1,504000, 200000);
fdelay_configure_output(&dev,3,1,500000, 200000);
fdelay_configure_output(&dev,4,1,500000, 200000);
}
\ No newline at end of file
#ifndef __ACAM_GPX_H
#define __ACAM_GPX_H
#define AR0_ROsc (1<<0)
#define AR0_RiseEn0 (1<<1)
#define AR0_FallEn0 (1<<2)
#define AR0_RiseEn1 (1<<3)
#define AR0_FallEn1 (1<<4)
#define AR0_RiseEn2 (1<<5)
#define AR0_FallEn2 (1<<6)
#define AR0_HQSel (1<<7)
#define AR0_TRiseEn(port) (1<<(10+port))
#define AR0_TFallEn(port) (1<<(19+port))
#define AR1_Adj(chan, value) (((value) & 0xf) << (chan * 4))
#define AR2_GMode (1<<0)
#define AR2_IMode (1<<1)
#define AR2_RMode (1<<2)
#define AR2_Disable(chan) (1<<(3+chan))
#define AR2_Adj(chan, value) (((value)&0xf)<<(12+4*(chan-7)))
#define AR3_RaSpeed(num,val) (val << (num*2 + 21))
#define AR3_Zero (0) // nothing interesting for the Fine Delay
#define AR4_StartTimer(value) ((value) & 0xff)
#define AR4_Quiet (1<<8)
#define AR4_MMode (1<<9)
#define AR4_MasterReset (1<<22)
#define AR4_PartialReset (1<<23)
#define AR4_AluTrigSoft (1<<24)
#define AR4_EFlagHiZN (1<<25)
#define AR4_MTimerStart (1<<26)
#define AR4_MTimerStop (1<<27)
#define AR5_StartOff1(value) ((value)&0x3ffff)
#define AR5_StopDisStart (1<<21)
#define AR5_StartDisStart (1<<22)
#define AR5_MasterAluTrig (1<<23)
#define AR5_PartialAluTrig (1<<24)
#define AR5_MasterOenTrig (1<<25)
#define AR5_PartialOenTrig (1<<26)
#define AR5_StartRetrig (1<<27)
#define AR6_Fill(value) ((value)&0xff)
#define AR6_StartOff2(value) (((value)&0x3ffff)<<8)
#define AR6_InSelECL (1<<26)
#define AR6_PowerOnECL (1<<27)
#define AR7_HSDiv(value) ((value)&0xff)
#define AR7_RefClkDiv(value) (((value)&0x7)<<8)
#define AR7_ResAdj (1<<11)
#define AR7_NegPhase (1<<12)
#define AR7_Track (1<<13)
#define AR7_MTimer(value) (((value) & 0x1ff)<<15)
#define AR14_16BitMode (1<<4)
#define AR8I_IFIFO1(reg) ((reg) & 0x1ffff)
#define AR8I_Slope1(reg) ((reg) & (1<<17) ? 1 : 0)
#define AR8I_StartN1(reg) (((reg) >> 18) & 0xff)
#define AR8I_ChaCode1(reg) (((reg) >> 26) & 0x3)
#define AR9I_IFIFO2(reg) ((reg) & 0x1ffff)
#define AR9I_Slope2(reg) ((reg) & (1<<17) ? 1 : 0)
#define AR9I_StartN2(reg) (((reg) >> 18) & 0xff)
#define AR9I_ChaCode2(reg) (((reg) >> 26) & 0x3)
#define AR8R_IFIFO1(reg) ((reg) & 0x3fffff)
#define AR9R_IFIFO2(reg) ((reg) & 0x3fffff)
#define AR11_StopCounter0(num) ((num) & 0xff)
#define AR11_StopCounter1(num) (((num) & 0xff) << 8)
#define AR11_HFifoErrU(num) (1 << (num+16))
#define AR11_IFifoErrU(num) (1 << (num+24))
#define AR11_NotLockErrU (1 << 26)
#define AR12_HFifoE (1<<11)
#define AR12_NotLocked (1<<10)
#endif
#ifndef __FD_LIB_H
#define __FD_LIB_H
#include <stdint.h>
/* Number of fractional bits in the timestamps/time definitions. Must be consistent with the HDL bitstream. */
#define FDELAY_FRAC_BITS 12
/* Hardware "handle" structure */
typedef struct fdelay_device
{
/* Base address of the FD core */
uint32_t base_addr;
/* Bus-specific readl/writel functions - so the same library can be used both with
RawRabbit, VME and Etherbone backends */
void (*writel)(void *priv, uint32_t data, uint32_t addr);
uint32_t (*readl)(void *priv, uint32_t addr);
void *priv_fd; /* pointer to Fine Delay library private data */
void *priv_io; /* pointer to the I/O routines private data */
} fdelay_device_t;
typedef struct
{
uint32_t utc;
uint32_t coarse;
uint32_t frac;
uint16_t seq_id;
} fdelay_time_t;
/*
--------------------
PUBLIC API
--------------------
*/
fdelay_time_t fdelay_from_picos(const uint64_t ps);
int64_t fdelay_to_picos(const fdelay_time_t t);
int fdelay_init(fdelay_device_t *dev);
int fdelay_release(fdelay_device_t *dev);
int fdelay_read(fdelay_device_t *dev, fdelay_time_t *timestamps, int how_many);
int fdelay_configure_trigger(fdelay_device_t *dev, int enable, int termination);
int fdelay_configure_output(fdelay_device_t *dev, int channel, int enable, int64_t delay_ps, int64_t width_ps);
#endif
/*
Register definitions for slave core: Fine Delay Wishbone slave
* File : fd_core.h
* Author : auto-generated by wbgen2 from fd_wishbone_slave.wb
* Created : Thu Oct 27 17:38:06 2011
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE fd_wishbone_slave.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_FD_WISHBONE_SLAVE_WB
#define __WBGEN2_REGDEFS_FD_WISHBONE_SLAVE_WB
#include <inttypes.h>
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: Reset Register */
/* definitions for register: ID Register */
/* definitions for register: Global Control Register */
/* definitions for field: Bypass delay block in reg: Global Control Register */
#define FD_GCR_BYPASS WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Enable trigger input in reg: Global Control Register */
#define FD_GCR_INPUT_EN WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Internal Counter Sync in reg: Global Control Register */
#define FD_GCR_CSYNC_INT WBGEN2_GEN_MASK(2, 1)
/* definitions for field: White Rabbit Counter Sync in reg: Global Control Register */
#define FD_GCR_CSYNC_WR WBGEN2_GEN_MASK(3, 1)
/* definitions for field: White Rabbit Timecode Ready in reg: Global Control Register */
#define FD_GCR_WR_READY WBGEN2_GEN_MASK(4, 1)
/* definitions for field: White Rabbit Locking Enable in reg: Global Control Register */
#define FD_GCR_WR_LOCK_EN WBGEN2_GEN_MASK(5, 1)
/* definitions for field: White Rabbit Oscillator Locked in reg: Global Control Register */
#define FD_GCR_WR_LOCKED WBGEN2_GEN_MASK(6, 1)
/* definitions for register: TDC Address/Data Register */
/* definitions for field: DATA in reg: TDC Address/Data Register */
#define FD_TAR_DATA_MASK WBGEN2_GEN_MASK(0, 28)
#define FD_TAR_DATA_SHIFT 0
#define FD_TAR_DATA_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define FD_TAR_DATA_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for field: ADDR in reg: TDC Address/Data Register */
#define FD_TAR_ADDR_MASK WBGEN2_GEN_MASK(28, 4)
#define FD_TAR_ADDR_SHIFT 28
#define FD_TAR_ADDR_W(value) WBGEN2_GEN_WRITE(value, 28, 4)
#define FD_TAR_ADDR_R(reg) WBGEN2_GEN_READ(reg, 28, 4)
/* definitions for register: TDC control/status reg */
/* definitions for field: Start TDC write in reg: TDC control/status reg */
#define FD_TDCSR_WRITE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Start TDC read in reg: TDC control/status reg */
#define FD_TDCSR_READ WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Error flag in reg: TDC control/status reg */
#define FD_TDCSR_ERR WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Interrupt flag in reg: TDC control/status reg */
#define FD_TDCSR_INT WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Load flag in reg: TDC control/status reg */
#define FD_TDCSR_LOAD WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Empty flag in reg: TDC control/status reg */
#define FD_TDCSR_EMPTY WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Start enable in reg: TDC control/status reg */
#define FD_TDCSR_STOP_EN WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Start disable in reg: TDC control/status reg */
#define FD_TDCSR_START_DIS WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Stop enable in reg: TDC control/status reg */
#define FD_TDCSR_START_EN WBGEN2_GEN_MASK(8, 1)
/* definitions for field: Stop disable in reg: TDC control/status reg */
#define FD_TDCSR_STOP_DIS WBGEN2_GEN_MASK(9, 1)
/* definitions for field: write 1: Pulse the Alutrigger line in reg: TDC control/status reg */
#define FD_TDCSR_ALUTRIG WBGEN2_GEN_MASK(10, 1)
/* definitions for register: Calibration register */
/* definitions for field: Triggers calibration pulses in reg: Calibration register */
#define FD_CALR_CAL_PULSE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Enable pulse generation in reg: Calibration register */
#define FD_CALR_PSEL_MASK WBGEN2_GEN_MASK(1, 4)
#define FD_CALR_PSEL_SHIFT 1
#define FD_CALR_PSEL_W(value) WBGEN2_GEN_WRITE(value, 1, 4)
#define FD_CALR_PSEL_R(reg) WBGEN2_GEN_READ(reg, 1, 4)
/* definitions for register: Acam to Delay line fractional part Scale Factor Register */
/* definitions for register: Acam Timestamp Merging Control Register */
/* definitions for field: Wraparound Coarse Threshold in reg: Acam Timestamp Merging Control Register */
#define FD_ATMCR_C_THR_MASK WBGEN2_GEN_MASK(0, 4)
#define FD_ATMCR_C_THR_SHIFT 0
#define FD_ATMCR_C_THR_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define FD_ATMCR_C_THR_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Wraparound Fine Threshold in reg: Acam Timestamp Merging Control Register */
#define FD_ATMCR_F_THR_MASK WBGEN2_GEN_MASK(4, 23)
#define FD_ATMCR_F_THR_SHIFT 4
#define FD_ATMCR_F_THR_W(value) WBGEN2_GEN_WRITE(value, 4, 23)
#define FD_ATMCR_F_THR_R(reg) WBGEN2_GEN_READ(reg, 4, 23)
/* definitions for register: Acam Start Offset Register */
/* definitions for field: Start Offset in reg: Acam Start Offset Register */
#define FD_ASOR_OFFSET_MASK WBGEN2_GEN_MASK(0, 23)
#define FD_ASOR_OFFSET_SHIFT 0
#define FD_ASOR_OFFSET_W(value) WBGEN2_GEN_WRITE(value, 0, 23)
#define FD_ASOR_OFFSET_R(reg) WBGEN2_GEN_READ(reg, 0, 23)
/* definitions for register: Raw Input Events Counter Register */
/* definitions for register: Tagged Input Events Counter Register */
/* definitions for register: Input Event Processing Delay Register */
/* definitions for field: Reset stats in reg: Input Event Processing Delay Register */
#define FD_IEPD_RST_STAT WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Processing delay in reg: Input Event Processing Delay Register */
#define FD_IEPD_PDELAY_MASK WBGEN2_GEN_MASK(1, 8)
#define FD_IEPD_PDELAY_SHIFT 1
#define FD_IEPD_PDELAY_W(value) WBGEN2_GEN_WRITE(value, 1, 8)
#define FD_IEPD_PDELAY_R(reg) WBGEN2_GEN_READ(reg, 1, 8)
/* definitions for register: SPI Control Register */
/* definitions for field: Data in reg: SPI Control Register */
#define FD_SCR_DATA_MASK WBGEN2_GEN_MASK(0, 24)
#define FD_SCR_DATA_SHIFT 0
#define FD_SCR_DATA_W(value) WBGEN2_GEN_WRITE(value, 0, 24)
#define FD_SCR_DATA_R(reg) WBGEN2_GEN_READ(reg, 0, 24)
/* definitions for field: Select DAC in reg: SPI Control Register */
#define FD_SCR_SEL_DAC WBGEN2_GEN_MASK(24, 1)
/* definitions for field: Select PLL in reg: SPI Control Register */
#define FD_SCR_SEL_PLL WBGEN2_GEN_MASK(25, 1)
/* definitions for field: Select GPIO in reg: SPI Control Register */
#define FD_SCR_SEL_GPIO WBGEN2_GEN_MASK(26, 1)
/* definitions for field: Ready flag in reg: SPI Control Register */
#define FD_SCR_READY WBGEN2_GEN_MASK(27, 1)
/* definitions for field: Clock Polarity in reg: SPI Control Register */
#define FD_SCR_CPOL WBGEN2_GEN_MASK(28, 1)
/* definitions for field: Transfer Start in reg: SPI Control Register */
#define FD_SCR_START WBGEN2_GEN_MASK(29, 1)
/* definitions for register: Reference Clock Rate Register */
/* definitions for register: Reference Clock Frequency Register */
/* definitions for register: Timestamp Buffer Control Register */
/* definitions for field: Buffer enable in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_ENABLE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Buffer purge in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_PURGE WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Reset TS Sequence Number in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_RST_SEQ WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Buffer full in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_FULL WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Buffer empty in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_EMPTY WBGEN2_GEN_MASK(4, 1)
/* definitions for register: Timestamp Buffer Readout UTC Register */
/* definitions for register: Timestamp Buffer Readout Cycles Register */
/* definitions for register: Timestamp Buffer Readout Fine / Seq ID Register */
/* definitions for field: Fine Value [in phase units] in reg: Timestamp Buffer Readout Fine / Seq ID Register */
#define FD_TSBR_FID_FINE_MASK WBGEN2_GEN_MASK(0, 12)
#define FD_TSBR_FID_FINE_SHIFT 0
#define FD_TSBR_FID_FINE_W(value) WBGEN2_GEN_WRITE(value, 0, 12)
#define FD_TSBR_FID_FINE_R(reg) WBGEN2_GEN_READ(reg, 0, 12)
/* definitions for field: Timestamp Sequence ID in reg: Timestamp Buffer Readout Fine / Seq ID Register */
#define FD_TSBR_FID_SEQID_MASK WBGEN2_GEN_MASK(16, 16)
#define FD_TSBR_FID_SEQID_SHIFT 16
#define FD_TSBR_FID_SEQID_W(value) WBGEN2_GEN_WRITE(value, 16, 16)
#define FD_TSBR_FID_SEQID_R(reg) WBGEN2_GEN_READ(reg, 16, 16)
/* definitions for register: Delay Control Register (channel 1) */
/* definitions for field: Enable channel in reg: Delay Control Register (channel 1) */
#define FD_DCR1_ENABLE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Delay mode select in reg: Delay Control Register (channel 1) */
#define FD_DCR1_MODE WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Pulse generator arm in reg: Delay Control Register (channel 1) */
#define FD_DCR1_PG_ARM WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Pulse generator triggered in reg: Delay Control Register (channel 1) */
#define FD_DCR1_PG_TRIG WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Start Delay Update in reg: Delay Control Register (channel 1) */
#define FD_DCR1_UPDATE WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Delay Update Done in reg: Delay Control Register (channel 1) */
#define FD_DCR1_UPD_DONE WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Force Calibration Delay in reg: Delay Control Register (channel 1) */
#define FD_DCR1_FORCE_DLY WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Output Polarity in reg: Delay Control Register (channel 1) */
#define FD_DCR1_POL WBGEN2_GEN_MASK(7, 1)
/* definitions for register: Fine Range Register (channel 1) */
/* definitions for register: Pulse start time / offset (UTC part, channel 1) */
/* definitions for register: Pulse start time / offset (8 ns cycles, channel 1) */
/* definitions for register: Pulse start time / offset (sub-cycle fine part, channel 1) */
/* definitions for register: Pulse end time / offset (UTC part, channel 1) */
/* definitions for register: Pulse end time / offset (8 ns cycles, channel 1) */
/* definitions for register: Pulse end time / offset (sub-cycle fine part, channel 1) */
/* definitions for register: Delay Control Register (channel 2) */
/* definitions for field: Enable channel in reg: Delay Control Register (channel 2) */
#define FD_DCR2_ENABLE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Delay mode select in reg: Delay Control Register (channel 2) */
#define FD_DCR2_MODE WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Pulse generator arm in reg: Delay Control Register (channel 2) */
#define FD_DCR2_PG_ARM WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Pulse generator triggered in reg: Delay Control Register (channel 2) */
#define FD_DCR2_PG_TRIG WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Start Delay Update in reg: Delay Control Register (channel 2) */
#define FD_DCR2_UPDATE WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Delay Update Done in reg: Delay Control Register (channel 2) */
#define FD_DCR2_UPD_DONE WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Force Calibration Delay in reg: Delay Control Register (channel 2) */
#define FD_DCR2_FORCE_DLY WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Output Polarity in reg: Delay Control Register (channel 2) */
#define FD_DCR2_POL WBGEN2_GEN_MASK(7, 1)
/* definitions for register: Fine Range Register (channel 2) */
/* definitions for register: Pulse start time / offset (UTC part, channel 2) */
/* definitions for register: Pulse start time / offset (8 ns cycles, channel 2) */
/* definitions for register: Pulse start time / offset (sub-cycle fine part, channel 2) */
/* definitions for register: Pulse end time / offset (UTC part, channel 2) */
/* definitions for register: Pulse end time / offset (8 ns cycles, channel 2) */
/* definitions for register: Pulse end time / offset (sub-cycle fine part, channel 2) */
/* definitions for register: Delay Control Register (channel 3) */
/* definitions for field: Enable channel in reg: Delay Control Register (channel 3) */
#define FD_DCR3_ENABLE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Delay mode select in reg: Delay Control Register (channel 3) */
#define FD_DCR3_MODE WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Pulse generator arm in reg: Delay Control Register (channel 3) */
#define FD_DCR3_PG_ARM WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Pulse generator triggered in reg: Delay Control Register (channel 3) */
#define FD_DCR3_PG_TRIG WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Start Delay Update in reg: Delay Control Register (channel 3) */
#define FD_DCR3_UPDATE WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Delay Update Done in reg: Delay Control Register (channel 3) */
#define FD_DCR3_UPD_DONE WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Force Calibration Delay in reg: Delay Control Register (channel 3) */
#define FD_DCR3_FORCE_DLY WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Output Polarity in reg: Delay Control Register (channel 3) */
#define FD_DCR3_POL WBGEN2_GEN_MASK(7, 1)
/* definitions for register: Fine Range Register (channel 3) */
/* definitions for register: Pulse start time / offset (UTC part, channel 3) */
/* definitions for register: Pulse start time / offset (8 ns cycles, channel 3) */
/* definitions for register: Pulse start time / offset (sub-cycle fine part, channel 3) */
/* definitions for register: Pulse end time / offset (UTC part, channel 3) */
/* definitions for register: Pulse end time / offset (8 ns cycles, channel 3) */
/* definitions for register: Pulse end time / offset (sub-cycle fine part, channel 3) */
/* definitions for register: Delay Control Register (channel 4) */
/* definitions for field: Enable channel in reg: Delay Control Register (channel 4) */
#define FD_DCR4_ENABLE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Delay mode select in reg: Delay Control Register (channel 4) */
#define FD_DCR4_MODE WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Pulse generator arm in reg: Delay Control Register (channel 4) */
#define FD_DCR4_PG_ARM WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Pulse generator triggered in reg: Delay Control Register (channel 4) */
#define FD_DCR4_PG_TRIG WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Start Delay Update in reg: Delay Control Register (channel 4) */
#define FD_DCR4_UPDATE WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Delay Update Done in reg: Delay Control Register (channel 4) */
#define FD_DCR4_UPD_DONE WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Force Calibration Delay in reg: Delay Control Register (channel 4) */
#define FD_DCR4_FORCE_DLY WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Output Polarity in reg: Delay Control Register (channel 4) */
#define FD_DCR4_POL WBGEN2_GEN_MASK(7, 1)
/* definitions for register: Fine Range Register (channel 4) */
/* definitions for register: Pulse start time / offset (UTC part, channel 4) */
/* definitions for register: Pulse start time / offset (8 ns cycles, channel 4) */
/* definitions for register: Pulse start time / offset (sub-cycle fine part, channel 4) */
/* definitions for register: Pulse end time / offset (UTC part, channel 4) */
/* definitions for register: Pulse end time / offset (8 ns cycles, channel 4) */
/* definitions for register: Pulse end time / offset (sub-cycle fine part, channel 4) */
/* definitions for register: Interrupt disable register */
/* definitions for field: TS Buffer not empty in reg: Interrupt disable register */
#define FD_EIC_IDR_TS_BUF_NOTEMPTY WBGEN2_GEN_MASK(0, 1)
/* definitions for register: Interrupt enable register */
/* definitions for field: TS Buffer not empty in reg: Interrupt enable register */
#define FD_EIC_IER_TS_BUF_NOTEMPTY WBGEN2_GEN_MASK(0, 1)
/* definitions for register: Interrupt mask register */
/* definitions for field: TS Buffer not empty in reg: Interrupt mask register */
#define FD_EIC_IMR_TS_BUF_NOTEMPTY WBGEN2_GEN_MASK(0, 1)
/* definitions for register: Interrupt status register */
/* definitions for field: TS Buffer not empty in reg: Interrupt status register */
#define FD_EIC_ISR_TS_BUF_NOTEMPTY WBGEN2_GEN_MASK(0, 1)
/* definitions for register: FIFO 'RAW FIFO' data output register 0 */
/* definitions for field: RawFrac in reg: FIFO 'RAW FIFO' data output register 0 */
#define FD_RAWFIFO_R0_FRAC_MASK WBGEN2_GEN_MASK(0, 28)
#define FD_RAWFIFO_R0_FRAC_SHIFT 0
#define FD_RAWFIFO_R0_FRAC_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define FD_RAWFIFO_R0_FRAC_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: FIFO 'RAW FIFO' data output register 1 */
/* definitions for field: RawCoarse in reg: FIFO 'RAW FIFO' data output register 1 */
#define FD_RAWFIFO_R1_COARSE_MASK WBGEN2_GEN_MASK(0, 28)
#define FD_RAWFIFO_R1_COARSE_SHIFT 0
#define FD_RAWFIFO_R1_COARSE_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define FD_RAWFIFO_R1_COARSE_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for register: FIFO 'RAW FIFO' control/status register */
/* definitions for field: FIFO empty flag in reg: FIFO 'RAW FIFO' control/status register */
#define FD_RAWFIFO_CSR_EMPTY WBGEN2_GEN_MASK(17, 1)
/* [0x0]: REG Reset Register */
#define FD_REG_RSTR 0x00000000
/* [0x4]: REG ID Register */
#define FD_REG_IDR 0x00000004
/* [0x8]: REG Global Control Register */
#define FD_REG_GCR 0x00000008
/* [0xc]: REG TDC Address/Data Register */
#define FD_REG_TAR 0x0000000c
/* [0x10]: REG TDC control/status reg */
#define FD_REG_TDCSR 0x00000010
/* [0x14]: REG Calibration register */
#define FD_REG_CALR 0x00000014
/* [0x18]: REG Acam to Delay line fractional part Scale Factor Register */
#define FD_REG_ADSFR 0x00000018
/* [0x1c]: REG Acam Timestamp Merging Control Register */
#define FD_REG_ATMCR 0x0000001c
/* [0x20]: REG Acam Start Offset Register */
#define FD_REG_ASOR 0x00000020
/* [0x24]: REG Raw Input Events Counter Register */
#define FD_REG_IECRAW 0x00000024
/* [0x28]: REG Tagged Input Events Counter Register */
#define FD_REG_IECTAG 0x00000028
/* [0x2c]: REG Input Event Processing Delay Register */
#define FD_REG_IEPD 0x0000002c
/* [0x30]: REG SPI Control Register */
#define FD_REG_SCR 0x00000030
/* [0x34]: REG Reference Clock Rate Register */
#define FD_REG_RCRR 0x00000034
/* [0x38]: REG Reference Clock Frequency Register */
#define FD_REG_RCFR 0x00000038
/* [0x3c]: REG Timestamp Buffer Control Register */
#define FD_REG_TSBCR 0x0000003c
/* [0x40]: REG Timestamp Buffer Readout UTC Register */
#define FD_REG_TSBR_U 0x00000040
/* [0x44]: REG Timestamp Buffer Readout Cycles Register */
#define FD_REG_TSBR_C 0x00000044
/* [0x48]: REG Timestamp Buffer Readout Fine / Seq ID Register */
#define FD_REG_TSBR_FID 0x00000048
/* [0x60]: REG Delay Control Register (channel 1) */
#define FD_REG_DCR1 0x00000060
/* [0x64]: REG Fine Range Register (channel 1) */
#define FD_REG_FRR1 0x00000064
/* [0x68]: REG Pulse start time / offset (UTC part, channel 1) */
#define FD_REG_U_START1 0x00000068
/* [0x6c]: REG Pulse start time / offset (8 ns cycles, channel 1) */
#define FD_REG_C_START1 0x0000006c
/* [0x70]: REG Pulse start time / offset (sub-cycle fine part, channel 1) */
#define FD_REG_F_START1 0x00000070
/* [0x74]: REG Pulse end time / offset (UTC part, channel 1) */
#define FD_REG_U_END1 0x00000074
/* [0x78]: REG Pulse end time / offset (8 ns cycles, channel 1) */
#define FD_REG_C_END1 0x00000078
/* [0x7c]: REG Pulse end time / offset (sub-cycle fine part, channel 1) */
#define FD_REG_F_END1 0x0000007c
/* [0x80]: REG Delay Control Register (channel 2) */
#define FD_REG_DCR2 0x00000080
/* [0x84]: REG Fine Range Register (channel 2) */
#define FD_REG_FRR2 0x00000084
/* [0x88]: REG Pulse start time / offset (UTC part, channel 2) */
#define FD_REG_U_START2 0x00000088
/* [0x8c]: REG Pulse start time / offset (8 ns cycles, channel 2) */
#define FD_REG_C_START2 0x0000008c
/* [0x90]: REG Pulse start time / offset (sub-cycle fine part, channel 2) */
#define FD_REG_F_START2 0x00000090
/* [0x94]: REG Pulse end time / offset (UTC part, channel 2) */
#define FD_REG_U_END2 0x00000094
/* [0x98]: REG Pulse end time / offset (8 ns cycles, channel 2) */
#define FD_REG_C_END2 0x00000098
/* [0x9c]: REG Pulse end time / offset (sub-cycle fine part, channel 2) */
#define FD_REG_F_END2 0x0000009c
/* [0xa0]: REG Delay Control Register (channel 3) */
#define FD_REG_DCR3 0x000000a0
/* [0xa4]: REG Fine Range Register (channel 3) */
#define FD_REG_FRR3 0x000000a4
/* [0xa8]: REG Pulse start time / offset (UTC part, channel 3) */
#define FD_REG_U_START3 0x000000a8
/* [0xac]: REG Pulse start time / offset (8 ns cycles, channel 3) */
#define FD_REG_C_START3 0x000000ac
/* [0xb0]: REG Pulse start time / offset (sub-cycle fine part, channel 3) */
#define FD_REG_F_START3 0x000000b0
/* [0xb4]: REG Pulse end time / offset (UTC part, channel 3) */
#define FD_REG_U_END3 0x000000b4
/* [0xb8]: REG Pulse end time / offset (8 ns cycles, channel 3) */
#define FD_REG_C_END3 0x000000b8
/* [0xbc]: REG Pulse end time / offset (sub-cycle fine part, channel 3) */
#define FD_REG_F_END3 0x000000bc
/* [0xc0]: REG Delay Control Register (channel 4) */
#define FD_REG_DCR4 0x000000c0
/* [0xc4]: REG Fine Range Register (channel 4) */
#define FD_REG_FRR4 0x000000c4
/* [0xc8]: REG Pulse start time / offset (UTC part, channel 4) */
#define FD_REG_U_START4 0x000000c8
/* [0xcc]: REG Pulse start time / offset (8 ns cycles, channel 4) */
#define FD_REG_C_START4 0x000000cc
/* [0xd0]: REG Pulse start time / offset (sub-cycle fine part, channel 4) */
#define FD_REG_F_START4 0x000000d0
/* [0xd4]: REG Pulse end time / offset (UTC part, channel 4) */
#define FD_REG_U_END4 0x000000d4
/* [0xd8]: REG Pulse end time / offset (8 ns cycles, channel 4) */
#define FD_REG_C_END4 0x000000d8
/* [0xdc]: REG Pulse end time / offset (sub-cycle fine part, channel 4) */
#define FD_REG_F_END4 0x000000dc
/* [0xe0]: REG Interrupt disable register */
#define FD_REG_EIC_IDR 0x000000e0
/* [0xe4]: REG Interrupt enable register */
#define FD_REG_EIC_IER 0x000000e4
/* [0xe8]: REG Interrupt mask register */
#define FD_REG_EIC_IMR 0x000000e8
/* [0xec]: REG Interrupt status register */
#define FD_REG_EIC_ISR 0x000000ec
/* [0xf0]: REG FIFO 'RAW FIFO' data output register 0 */
#define FD_REG_RAWFIFO_R0 0x000000f0
/* [0xf4]: REG FIFO 'RAW FIFO' data output register 1 */
#define FD_REG_RAWFIFO_R1 0x000000f4
/* [0xf8]: REG FIFO 'RAW FIFO' control/status register */
#define FD_REG_RAWFIFO_CSR 0x000000f8
#endif
#ifndef __OPENCORES_SPI_H
#define __OPENCORES_SPI_H
#include <stdint.h>
#define OCSPI_REG_RX0 0
#define OCSPI_REG_RX1 4
#define OCSPI_REG_RX2 8
#define OCSPI_REG_RX3 12
#define OCSPI_REG_TX0 0
#define OCSPI_REG_TX1 4
#define OCSPI_REG_TX2 8
#define OCSPI_REG_TX3 12
#define OCSPI_REG_CTRL 16
#define OCSPI_REG_DIVIDER 20
#define OCSPI_REG_SS 24
#define OCSPI_CTRL_ASS (1<<13)
#define OCSPI_CTRL_IE (1<<12)
#define OCSPI_CTRL_LSB (1<<11)
#define OCSPI_CTRL_TXNEG (1<<10)
#define OCSPI_CTRL_RXNEG (1<<9)
#define OCSPI_CTRL_GO_BSY (1<<8)
#define OCSPI_CTRL_CHAR_LEN(x) ((x) & 0x7f)
#endif
const struct {int reg; uint8_t val; } ad9516_regs[] = {
{0x0000, 0x99},
{0x0001, 0x00},
{0x0002, 0x10},
{0x0003, 0xC3},
{0x0004, 0x00},
{0x0010, 0x7C},
{0x0011, 0x05},
{0x0012, 0x00},
{0x0013, 0x0C},
{0x0014, 0x12},
{0x0015, 0x00},
{0x0016, 0x05},
{0x0017, 0x00},
{0x0018, 0x07},
{0x0019, 0x00},
{0x001A, 0x00},
{0x001B, 0xE0},
{0x001C, 0x02},
{0x001D, 0x00},
{0x001E, 0x00},
{0x001F, 0x0E},
{0x00A0, 0x01},
{0x00A1, 0x00},
{0x00A2, 0x00},
{0x00A3, 0x01},
{0x00A4, 0x00},
{0x00A5, 0x00},
{0x00A6, 0x01},
{0x00A7, 0x00},
{0x00A8, 0x00},
{0x00A9, 0x01},
{0x00AA, 0x00},
{0x00AB, 0x00},
{0x00F0, 0x08},
{0x00F1, 0x08},
{0x00F2, 0x08},
{0x00F3, 0x08},
{0x00F4, 0x08},
{0x00F5, 0x08},
{0x0140, 0x5A},
{0x0141, 0x5A},
{0x0142, 0x5B},
{0x0143, 0x42},
{0x0190, 0x00},
{0x0191, 0x00},
{0x0192, 0x00},
{0x0193, 0x00},
{0x0194, 0x00},
{0x0195, 0x00},
{0x0196, 0xFF},
{0x0197, 0x00},
{0x0198, 0x00},
{0x0199, 0x33},
{0x019A, 0x00},
{0x019B, 0x11},
{0x019C, 0x20},
{0x019D, 0x00},
{0x019E, 0x00},
{0x019F, 0x00},
{0x01A0, 0x11},
{0x01A1, 0x20},
{0x01A2, 0x00},
{0x01A3, 0x00},
{0x01E0, 0x04},
{0x01E1, 0x02},
{0x0230, 0x00},
{0x0231, 0x00},
{0x0232, 0x00},
{-1, 0}};
#ifndef __RR_IO_H
#define __RR_IO_H
#include <stdint.h>
int rr_init();
int rr_writel(uint32_t data, uint32_t addr);
uint32_t rr_readl(uint32_t addr);
int rr_load_bitstream(const void *data, int size8);
int rr_load_bitstream_from_file(const char *file_name);
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <rawrabbit.h>
#include "rr_io.h"
#define DEVNAME "/dev/rawrabbit"
static int fd;
int rr_init()
{
struct rr_devsel devsel;
int ret = -EINVAL;
fd = open(DEVNAME, O_RDWR);
if (fd < 0) {
return -1;
}
return 0;
}
int rr_writel(uint32_t data, uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(0);
iocmd.data32 = data;
ioctl(fd, RR_WRITE, &iocmd);
}
uint32_t rr_readl(uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(0);
ioctl(fd, RR_READ, &iocmd);
return iocmd.data32;
}
static void gennum_writel(uint32_t data, uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(4);
iocmd.data32 = data;
ioctl(fd, RR_WRITE, &iocmd);
}
static uint32_t gennum_readl(uint32_t addr)
{
struct rr_iocmd iocmd;
iocmd.datasize = 4;
iocmd.address = addr;
iocmd.address |= __RR_SET_BAR(4);
ioctl(fd, RR_READ, &iocmd);
return iocmd.data32;
}
static inline int64_t get_tics()
{
struct timezone tz= {0,0};
struct timeval tv;
gettimeofday(&tv, &tz);
return (int64_t)tv.tv_sec * 1000000LL + (int64_t) tv.tv_usec;
}
int rr_load_bitstream(const void *data, int size8)
{
int i, ctrl, done = 0, wrote = 0;
unsigned long j;
uint8_t val8;
const uint8_t *data8 = data;
const uint32_t *data32 = data;
int size32 = (size8 + 3) >> 2;
// fprintf(stderr,"Loading %d bytes...\n", size8);
if (1) {
/*
* Hmmm.... revers bits for xilinx images?
* We can't do in kernel space anyways, as the pages are RO
*/
uint8_t *d8 = (uint8_t *)data8; /* Horrible: kill const */
for (i = 0; i < size8; i++) {
val8 = d8[i];
d8[i] = 0
| ((val8 & 0x80) >> 7)
| ((val8 & 0x40) >> 5)
| ((val8 & 0x20) >> 3)
| ((val8 & 0x10) >> 1)
| ((val8 & 0x08) << 1)
| ((val8 & 0x04) << 3)
| ((val8 & 0x02) << 5)
| ((val8 & 0x01) << 7);
}
}
/* Do real stuff */
gennum_writel(0x00, FCL_CLK_DIV);
gennum_writel(0x40, FCL_CTRL); /* Reset */
i = gennum_readl( FCL_CTRL);
if (i != 0x40) {
fprintf(stderr, "%s: %i: error\n", __func__, __LINE__);
return;
}
gennum_writel(0x00, FCL_CTRL);
gennum_writel(0x00, FCL_IRQ); /* clear pending irq */
switch(size8 & 3) {
case 3: ctrl = 0x116; break;
case 2: ctrl = 0x126; break;
case 1: ctrl = 0x136; break;
case 0: ctrl = 0x106; break;
}
gennum_writel(ctrl, FCL_CTRL);
gennum_writel(0x00, FCL_CLK_DIV); /* again? maybe 1 or 2? */
gennum_writel(0x00, FCL_TIMER_CTRL); /* "disable FCL timer func" */
gennum_writel(0x10, FCL_TIMER_0); /* "pulse width" */
gennum_writel(0x00, FCL_TIMER_1);
/* Set delay before data and clock is applied by FCL after SPRI_STATUS is
detected being assert.
*/
gennum_writel(0x08, FCL_TIMER2_0); /* "delay before data/clock..." */
gennum_writel(0x00, FCL_TIMER2_1);
gennum_writel(0x17, FCL_EN); /* "output enable" */
ctrl |= 0x01; /* "start FSM configuration" */
gennum_writel(ctrl, FCL_CTRL);
while(size32 > 0)
{
/* Check to see if FPGA configuation has error */
i = gennum_readl( FCL_IRQ);
if ( (i & 8) && wrote) {
done = 1;
fprintf(stderr,"EarlyDone");
// fprintf(stderr,"%s: %idone after %i\n", __func__, __LINE__, wrote);
} else if ( (i & 0x4) && !done) {
fprintf(stderr,"Error after %i\n", wrote);
return -1;
}
// fprintf(stderr,".");
while(gennum_readl(FCL_IRQ) & (1<<5)); // wait until at least 1/2 of the FIFO is empty
/* Write 64 dwords into FIFO at a time. */
for (i = 0; size32 && i < 32; i++) {
gennum_writel(*data32, FCL_FIFO);
data32++; size32--; wrote++;
// udelay(20);
}
}
gennum_writel(0x186, FCL_CTRL); /* "last data written" */
int64_t tstart = get_tics();
//j = jiffies + 2 * HZ;
/* Wait for DONE interrupt */
while(!done) {
// fprintf(stderr,stderr, "Wait!");
i = gennum_readl( FCL_IRQ);
printf("irqr: %x %x\n", FCL_IRQ, i);
if (i & 0x8) {
fprintf(stderr,"done after %i\n", wrote);
done = 1;
} else if( (i & 0x4) && !done) {
fprintf(stderr,"Error after %i\n", wrote);
return -1;
}
usleep(10000);
if(get_tics() - tstart > 1000000LL)
{
fprintf(stderr,"Loader: DONE timeout. Did you choose proper bitgen options?\n");
return;
}
/*if (time_after(jiffies, j)) {
printk("%s: %i: tout after %i\n", __func__, __LINE__,
wrote);
return;
} */
}
return done?0:-1;
}
int rr_load_bitstream_from_file(const char *file_name)
{
uint8_t *buf;
FILE *f;
uint32_t size;
f=fopen(file_name,"rb");
if(!f) return -1;
fseek(f, 0, SEEK_END);
size = ftell(f);
buf = malloc(size);
if(!buf)
{
fclose(f);
return -1;
}
fseek(f, 0, SEEK_SET);
fread(buf, 1, size, f);
fclose(f);
int rval = rr_load_bitstream(buf, size);
free(buf);
return rval;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment