diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c7fe7d..d6942e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: - name: Build-Linux-32b-arm if: matrix.platform == 'ubuntu-latest' run: | - GOARCH=arm CGO_ENABLED=0 \ + GOARCH=arm CGO_ENABLED=1 \ CC=arm-linux-gnueabihf-gcc \ CC_FOR_TARGET=arm-linux-gnueabihf-gcc \ go install -v ./... diff --git a/eda/alt_types.h b/eda/alt_types.h new file mode 100644 index 0000000..8461717 --- /dev/null +++ b/eda/alt_types.h @@ -0,0 +1,54 @@ +#ifndef __ALT_TYPES_H__ +#define __ALT_TYPES_H__ + +/****************************************************************************** + * * + * License Agreement * + * * + * Copyright (c) 2009 Altera Corporation, San Jose, California, USA. * + * All rights reserved. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * * + * This agreement shall be governed in all respects by the laws of the State * + * of California and by the laws of the United States of America. * + * * + * Altera does not recommend, suggest or require that this reference design * + * file be used in conjunction or combination with any other product. * + ******************************************************************************/ + +/* + * Don't declare these typedefs if this file is included by assembly source. + */ +#ifndef ALT_ASM_SRC +typedef signed char alt_8; +typedef unsigned char alt_u8; +typedef signed short alt_16; +typedef unsigned short alt_u16; +typedef signed long alt_32; +typedef unsigned long alt_u32; +typedef long long alt_64; +typedef unsigned long long alt_u64; +#endif + +#define ALT_INLINE __inline__ +#define ALT_ALWAYS_INLINE __attribute__((always_inline)) +#define ALT_WEAK __attribute__((weak)) + +#endif /* __ALT_TYPES_H__ */ diff --git a/eda/altera_avalon_fifo_regs.h b/eda/altera_avalon_fifo_regs.h new file mode 100644 index 0000000..7fd906c --- /dev/null +++ b/eda/altera_avalon_fifo_regs.h @@ -0,0 +1,115 @@ +/****************************************************************************** + * * + * License Agreement * + * * + * Copyright (c) 2006 Altera Corporation, San Jose, California, USA. * + * All rights reserved. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * * + * This agreement shall be governed in all respects by the laws of the State * + * of California and by the laws of the United States of America. * + * * + ******************************************************************************/ + +#ifndef __ALTERA_AVALON_FIFO_REGS_H__ +#define __ALTERA_AVALON_FIFO_REGS_H__ + +#include + +#define ALTERA_AVALON_FIFO_OTHER_INFO_REG 1 +#define ALTERA_AVALON_FIFO_DATA_REG 0 + +#define ALTERA_AVALON_FIFO_LEVEL_REG 0 +#define ALTERA_AVALON_FIFO_STATUS_REG 1 +#define ALTERA_AVALON_FIFO_EVENT_REG 2 +#define ALTERA_AVALON_FIFO_IENABLE_REG 3 +#define ALTERA_AVALON_FIFO_ALMOSTFULL_REG 4 +#define ALTERA_AVALON_FIFO_ALMOSTEMPTY_REG 5 + +// Read slave +#define IORD_ALTERA_AVALON_FIFO_DATA(base) \ + IORD(base, ALTERA_AVALON_FIFO_DATA_REG) + +#define IORD_ALTERA_AVALON_FIFO_OTHER_INFO(base) \ + IORD(base, ALTERA_AVALON_FIFO_OTHER_INFO_REG) + +// Write slave +#define IOWR_ALTERA_AVALON_FIFO_DATA(base, data) \ + IOWR(base, ALTERA_AVALON_FIFO_DATA_REG, data) + +#define IOWR_ALTERA_AVALON_FIFO_OTHER_INFO(base, data) \ + IOWR(base, ALTERA_AVALON_FIFO_OTHER_INFO_REG, data) + +// Control slave +#define IORD_ALTERA_AVALON_FIFO_LEVEL(base) \ + IORD(base, ALTERA_AVALON_FIFO_LEVEL_REG) + +#define IORD_ALTERA_AVALON_FIFO_STATUS(base) \ + IORD(base, ALTERA_AVALON_FIFO_STATUS_REG) + +#define IORD_ALTERA_AVALON_FIFO_EVENT(base) \ + IORD(base, ALTERA_AVALON_FIFO_EVENT_REG) + +#define IORD_ALTERA_AVALON_FIFO_IENABLE(base) \ + IORD(base, ALTERA_AVALON_FIFO_IENABLE_REG) + +#define IORD_ALTERA_AVALON_FIFO_ALMOSTFULL(base) \ + IORD(base, ALTERA_AVALON_FIFO_ALMOSTFULL_REG) + +#define IORD_ALTERA_AVALON_FIFO_ALMOSTEMPTY(base) \ + IORD(base, ALTERA_AVALON_FIFO_ALMOSTEMPTY_REG) + +#define IOWR_ALTERA_AVALON_FIFO_EVENT(base, data) \ + IOWR(base, ALTERA_AVALON_FIFO_EVENT_REG, data) + +#define IOWR_ALTERA_AVALON_FIFO_IENABLE(base, data) \ + IOWR(base, ALTERA_AVALON_FIFO_IENABLE_REG, data) + +#define IOWR_ALTERA_AVALON_FIFO_ALMOSTFULL(base, data) \ + IOWR(base, ALTERA_AVALON_FIFO_ALMOSTFULL_REG, data) + +#define IOWR_ALTERA_AVALON_FIFO_ALMOSTEMPTY(base, data) \ + IOWR(base, ALTERA_AVALON_FIFO_ALMOSTEMPTY_REG, data) + +#define ALTERA_AVALON_FIFO_EVENT_F_MSK (0x01) +#define ALTERA_AVALON_FIFO_EVENT_E_MSK (0x02) +#define ALTERA_AVALON_FIFO_EVENT_AF_MSK (0x04) +#define ALTERA_AVALON_FIFO_EVENT_AE_MSK (0x08) +#define ALTERA_AVALON_FIFO_EVENT_OVF_MSK (0x10) +#define ALTERA_AVALON_FIFO_EVENT_UDF_MSK (0x20) +#define ALTERA_AVALON_FIFO_EVENT_ALL (0x3F) + +#define ALTERA_AVALON_FIFO_STATUS_F_MSK (0x01) +#define ALTERA_AVALON_FIFO_STATUS_E_MSK (0x02) +#define ALTERA_AVALON_FIFO_STATUS_AF_MSK (0x04) +#define ALTERA_AVALON_FIFO_STATUS_AE_MSK (0x08) +#define ALTERA_AVALON_FIFO_STATUS_OVF_MSK (0x10) +#define ALTERA_AVALON_FIFO_STATUS_UDF_MSK (0x20) +#define ALTERA_AVALON_FIFO_STATUS_ALL (0x3F) + +#define ALTERA_AVALON_FIFO_IENABLE_F_MSK (0x01) +#define ALTERA_AVALON_FIFO_IENABLE_E_MSK (0x02) +#define ALTERA_AVALON_FIFO_IENABLE_AF_MSK (0x04) +#define ALTERA_AVALON_FIFO_IENABLE_AE_MSK (0x08) +#define ALTERA_AVALON_FIFO_IENABLE_OVF_MSK (0x10) +#define ALTERA_AVALON_FIFO_IENABLE_UDF_MSK (0x20) +#define ALTERA_AVALON_FIFO_IENABLE_ALL (0x3F) + +#endif /* __ALTERA_AVALON_FIFO_REGS_H__ */ diff --git a/eda/cdevice.go b/eda/cdevice.go new file mode 100644 index 0000000..3e0f1d9 --- /dev/null +++ b/eda/cdevice.go @@ -0,0 +1,303 @@ +// Copyright 2021 The go-lpc Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package eda + +/** TO DO LIST + * + * change HR_id numbering (1 to 8 instead of 0 to 7) (+ processing script) + * change trigger_id numbering (start 1) (+ processing script) + * + */ + +//#cgo CFLAGS: -g -Wall -std=c99 -D_GNU_SOURCE=1 -I. +//#cgo CFLAGS arm: -I/build/soc_eds/ip/altera/hps/altera_hps/hwlib/include +//#cgo LDFLAGS arm: -static +// +//#include +//#include +//#include +//#include "device.h" +//#include "fpga.h" +import "C" + +import ( + "fmt" + "net" + "strconv" + "time" + "unsafe" + + "github.com/go-lpc/mim/conddb" +) + +type cdevice struct { + ctx *C.Device_t + rfms []int // list of enabled RFMs + difs map[int]uint8 // map of EDA-slot->DIF/RFM-id + cfg config + + done chan int // signal to stop DAQ +} + +var _ device = (*cdevice)(nil) + +func newCDevice(devmem, odir, devshm string, opts ...Option) (*cdevice, error) { + ctx := C.new_device() + if ctx == nil { + return nil, fmt.Errorf("ceda: could not create EDA device") + } + + dev := &cdevice{ + ctx: ctx, + cfg: newConfig(), + } + WithResetBCID(10 * time.Second)(&dev.cfg) + WithDevSHM(devshm)(&dev.cfg) + + for _, opt := range opts { + opt(&dev.cfg) + } + + // setup RFMs indices from provided mask + dev.rfms = nil + for i := 0; i < nRFM; i++ { + if (dev.cfg.daq.rfm>>i)&1 == 1 { + dev.rfms = append(dev.rfms, i) + } + } + + return dev, nil +} + +func (dev *cdevice) Close() error { + if dev.ctx == nil { + return nil + } + C.device_free(dev.ctx) + dev.ctx = nil + return nil +} + +func (dev *cdevice) Boot(cfg []conddb.RFM) error { + dev.rfms = nil + dev.difs = make(map[int]uint8, nRFM) + dev.cfg.daq.rfm = 0 + var trig uint8 + switch dev.cfg.daq.mode { + case "dcc": + trig = 0 + case "noise": + trig = 1 + default: + return fmt.Errorf("eda: unknown trig-mode: %q", dev.cfg.mode) + } + for _, rfm := range cfg { + rc := C.device_boot_rfm( + dev.ctx, + C.uint8_t(rfm.ID), C.int(rfm.Slot), + C.uint32_t(rfm.DAQ.RShaper), + C.uint32_t(trig), + ) + if rc != 0 { + return fmt.Errorf( + "ceda: could not boot RFM=%d slot=%d: err=%v", + rfm.ID, rfm.Slot, rc, + ) + } + dev.rfms = append(dev.rfms, rfm.Slot) + dev.difs[rfm.Slot] = uint8(rfm.ID) + dev.cfg.daq.rfm |= (1 << rfm.Slot) + dev.cfg.hr.rshaper = uint32(rfm.DAQ.RShaper) + } + return nil +} + +func (dev *cdevice) ConfigureDIF(addr string, dif uint8, asics []conddb.ASIC) error { + // FIXME(sbinet): handle hysteresis, make sure addrs are unique. + dev.cfg.daq.addrs = append(dev.cfg.daq.addrs, addr) + + dev.setDBConfig(dif, asics) + + err := dev.configASICs(dif) + if err != nil { + return fmt.Errorf("eda: could not configure DIF=%d: %w", dif, err) + } + + host, port_, err := net.SplitHostPort(addr) + if err != nil { + return fmt.Errorf( + "ceda: could not infer rfm-socket host/port from %q: %w", + addr, err, + ) + } + + c_addr := C.CString(host) + defer C.free(unsafe.Pointer(c_addr)) + + port, err := strconv.Atoi(port_) + if err != nil { + return fmt.Errorf( + "ceda: could not infer rfm-socket port from %q: %w", addr, err, + ) + } + + rc := C.device_configure_dif(dev.ctx, C.uint8_t(dif), c_addr, C.int(port)) + if rc != 0 { + return fmt.Errorf("ceda: could not configure EDA-DIF=%d: rc=%d", dif, rc) + } + + return nil +} + +func (dev *cdevice) setDBConfig(dif uint8, asics []conddb.ASIC) { + dev.cfg.mode = "db" + dev.cfg.hr.db.asics[dif] = make([]conddb.ASIC, len(asics)) + copy(dev.cfg.hr.db.asics[dif], asics) +} + +func (dev *cdevice) configASICs(dif uint8) error { + asics, ok := dev.cfg.hr.db.asics[dif] + if !ok { + return fmt.Errorf("ceda: could not find conddb configuration for DIF=%d", dif) + } + + for i, asic := range asics { + var ( + cfg = asic.HRConfig() + ihr = uint32(i) + n = len(cfg) + ) + for i, v := range cfg { + dev.hrscSetBit(ihr, uint32(n-1-i), uint32(v)) + } + } + return nil +} + +func (dev *cdevice) Initialize() error { + rc := C.device_init_mmap(dev.ctx) + if rc != 0 { + return fmt.Errorf("ceda: could not initialize EDA device mmap rc=%d", rc) + } + + rc = C.device_init_fpga(dev.ctx) + if rc != 0 { + return fmt.Errorf("ceda: could not initialize EDA device FPGA rc=%d", rc) + } + + if dev.cfg.mode == "db" { + err := dev.initHRTables() + if err != nil { + return fmt.Errorf("ceda: could not initialize EDA HRSC tables: %w", err) + } + } + + rc = C.device_init_hrsc(dev.ctx) + if rc != 0 { + return fmt.Errorf("ceda: could not initialize EDA device HRSC rc=%d", rc) + } + + rc = C.device_init_scks(dev.ctx) + if rc != 0 { + return fmt.Errorf("ceda: could not initialize EDA device SCKS rc=%d", rc) + } + + return nil +} + +func (dev *cdevice) initHRTables() error { + // for each active RFM, tune the configuration. + for i, slot := range dev.rfms { + rfm := uint32(dev.rfms[i]) + dif := dev.difs[slot] + asics := dev.cfg.hr.db.asics[dif] + // mask unused channels + for hr := uint32(0); hr < nHR; hr++ { + for ch := uint32(0); ch < nChans; ch++ { + m0 := bitU64(asics[hr].Mask0, ch) + m1 := bitU64(asics[hr].Mask1, ch) + m2 := bitU64(asics[hr].Mask2, ch) + + mask := uint32(m0 | m1<<1 | m2<<2) + dev.cfg.mask.table[64*(nHR*rfm+hr)+ch] = mask + } + } + + // set DAC thresholds + for hr := uint32(0); hr < nHR; hr++ { + th0 := uint32(asics[hr].B0) + th1 := uint32(asics[hr].B1) + th2 := uint32(asics[hr].B2) + + dev.cfg.daq.floor[3*(nHR*rfm+hr)+0] = th0 + dev.cfg.daq.floor[3*(nHR*rfm+hr)+1] = th1 + dev.cfg.daq.floor[3*(nHR*rfm+hr)+2] = th2 + } + + // set preamplifier gain + for hr := uint32(0); hr < nHR; hr++ { + for ch := uint32(0); ch < nChans; ch++ { + v, err := strconv.ParseUint(string(asics[hr].PreAmpGain[2*ch:2*ch+2]), 16, 8) + if err != nil { + return err + } + gain := uint32(v) + dev.cfg.preamp.gains[64*(nHR*rfm+hr)+ch] = gain + } + } + } + + memcpy := func(dst *C.alt_u32, src []uint32) { + C.memcpy( + unsafe.Pointer(dst), + unsafe.Pointer(&src[0]), + C.size_t(len(src)*int(unsafe.Sizeof(src[0]))), + ) + } + memcpy(&dev.ctx.dac_floor_table[0], dev.cfg.daq.floor[:]) + memcpy(&dev.ctx.pa_gain_table[0], dev.cfg.preamp.gains[:]) + memcpy(&dev.ctx.mask_table[0], dev.cfg.mask.table[:]) + return nil +} + +func (dev *cdevice) Start(run uint32) error { + rc := C.device_start(dev.ctx, C.uint32_t(run)) + if rc != 0 { + return fmt.Errorf("ceda: could not start EDA device: rc=%d", rc) + } + go dev.loop() + return nil +} + +func (dev *cdevice) loop() { + dev.done = make(chan int) + defer close(dev.done) + + C.device_loop(dev.ctx) +} + +func (dev *cdevice) Stop() error { + const timeout = 10 * time.Second + tck := time.NewTimer(timeout) + defer tck.Stop() + + C.device_stop_loop(dev.ctx) + + select { + case <-dev.done: + case <-tck.C: + return fmt.Errorf("ceda: could not stop DAQ (timeout=%v)", timeout) + } + + rc := C.device_stop(dev.ctx) + if rc != 0 { + return fmt.Errorf("ceda: could not stop EDA device: rc=%d", rc) + } + return nil +} + +func (dev *cdevice) hrscSetBit(hr, addr, bit uint32) { + C.HRSC_set_bit(C.alt_u32(hr), C.alt_u32(addr), C.alt_u32(bit)) +} diff --git a/eda/config.c b/eda/config.c new file mode 100644 index 0000000..9159875 --- /dev/null +++ b/eda/config.c @@ -0,0 +1,189 @@ +// Copyright 2021 The tomuvol-daq Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include "config.h" +#include "device.h" +#include "logger.h" + +int read_th_offset(FILE *dac_floor_file, alt_u32 *dac_floor_table) { + ssize_t n = 0; + int rfm_index, hr_addr; + int dac_floor; + int delimiter = ';'; + char *str = NULL; + size_t len = 0; + for (rfm_index = 0; rfm_index < NB_RFM; rfm_index++) { + for (hr_addr = 0; hr_addr < NB_HR; hr_addr++) { + n = getdelim(&str, &len, delimiter, dac_floor_file); + if (n == 0) { + continue; + } + while (str[0] == '#') { // if comment line, skip + n = getline(&str, &len, dac_floor_file); + if (n == 0) { + continue; + } + n = getdelim(&str, &len, delimiter, dac_floor_file); + if (n == 0) { + continue; + } + } + if (atoi(str) != rfm_index) { + log_printf("error parsing dac_floor file\n"); + log_flush(); + return -1; + } + n = getdelim(&str, &len, delimiter, dac_floor_file); + if (n == 0) { + continue; + } + if (atoi(str) != hr_addr) { + log_printf("error parsing dac_floor file\n"); + return -1; + } + // dac0 + n = getdelim(&str, &len, delimiter, dac_floor_file); + if (n == 0) { + continue; + } + dac_floor = atoi(str); + dac_floor_table[3 * (NB_HR * rfm_index + hr_addr)] = dac_floor; + // dac1 + n = getdelim(&str, &len, delimiter, dac_floor_file); + if (n == 0) { + continue; + } + dac_floor = atoi(str); + dac_floor_table[3 * (NB_HR * rfm_index + hr_addr) + 1] = dac_floor; + // dac2 + n = getline(&str, &len, dac_floor_file); + if (n == 0) { + continue; + } + dac_floor = atoi(str); + dac_floor_table[3 * (NB_HR * rfm_index + hr_addr) + 2] = dac_floor; + } + } + return 1; +} + +int read_pa_gain(FILE *pa_gain_file, alt_u32 *pa_gain_table) { + ssize_t n = 0; + int rfm_index, hr_addr, chan; + int pa_gain; + int delimiter = ';'; + char *str = NULL; + size_t len = 0; + for (rfm_index = 0; rfm_index < NB_RFM; rfm_index++) { + for (hr_addr = 0; hr_addr < NB_HR; hr_addr++) { + for (chan = 0; chan < 64; chan++) { + n = getdelim(&str, &len, delimiter, pa_gain_file); + if (n == 0) { + continue; + } + while (str[0] == '#') { // if comment line, skip + n = getline(&str, &len, pa_gain_file); + if (n == 0) { + continue; + } + n = getdelim(&str, &len, delimiter, pa_gain_file); + if (n == 0) { + continue; + } + } + if (atoi(str) != rfm_index) { + log_printf("error parsing dac_floor file\n"); + log_flush(); + return -1; + } + n = getdelim(&str, &len, delimiter, pa_gain_file); + if (n == 0) { + continue; + } + if (atoi(str) != hr_addr) { + log_printf("error parsing pa_gain file\n"); + log_flush(); + return -1; + } + n = getdelim(&str, &len, delimiter, pa_gain_file); + if (n == 0) { + continue; + } + if (atoi(str) != chan) { + log_printf("error parsing pa_gain file\n"); + log_flush(); + return -1; + } + n = getline(&str, &len, pa_gain_file); + if (n == 0) { + continue; + } + pa_gain = atoi(str); + pa_gain_table[64 * (NB_HR * rfm_index + hr_addr) + chan] = pa_gain; + } + } + } + return 1; +} + +int read_mask(FILE *mask_file, alt_u32 *mask_table) { + ssize_t n = 0; + int rfm_index, hr_addr, chan; + int mask; + int delimiter = ';'; + char *str = NULL; + size_t len = 0; + for (rfm_index = 0; rfm_index < NB_RFM; rfm_index++) { + for (hr_addr = 0; hr_addr < NB_HR; hr_addr++) { + for (chan = 0; chan < 64; chan++) { + n = getdelim(&str, &len, delimiter, mask_file); + if (n == 0) { + continue; + } + while (str[0] == '#') { // if comment line, skip + n = getline(&str, &len, mask_file); + if (n == 0) { + continue; + } + n = getdelim(&str, &len, delimiter, mask_file); + if (n == 0) { + continue; + } + } + if (atoi(str) != rfm_index) { + log_printf("error parsing dac_floor file\n"); + log_flush(); + return -1; + } + n = getdelim(&str, &len, delimiter, mask_file); + if (n == 0) { + continue; + } + if (atoi(str) != hr_addr) { + log_printf("error parsing pa_gain file\n"); + log_flush(); + return -1; + } + n = getdelim(&str, &len, delimiter, mask_file); + if (n == 0) { + continue; + } + if (atoi(str) != chan) { + log_printf("error parsing pa_gain file\n"); + log_flush(); + return -1; + } + n = getline(&str, &len, mask_file); + if (n == 0) { + continue; + } + mask = atoi(str); + mask_table[64 * (NB_HR * rfm_index + hr_addr) + chan] = mask; + } + } + } + return 1; +} diff --git a/eda/config.h b/eda/config.h new file mode 100644 index 0000000..7d303ed --- /dev/null +++ b/eda/config.h @@ -0,0 +1,18 @@ +#ifndef EDA_CONFIG_H +#define EDA_CONFIG_H 1 + +// Copyright 2021 The tomuvol-daq Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +#include "alt_types.h" + +int read_th_offset(FILE *dac_floor_file, alt_u32 *dac_floor_table); + +int read_pa_gain(FILE *pa_gain_file, alt_u32 *pa_gain_table); + +int read_mask(FILE *mask_file, alt_u32 *mask_table); + +#endif // !EDA_CONFIG_H diff --git a/eda/device.c b/eda/device.c new file mode 100644 index 0000000..d7b0fe9 --- /dev/null +++ b/eda/device.c @@ -0,0 +1,945 @@ +/* + * Copyright 2020 + * Baptiste Joly + * Guillaume Blanchard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +/** + * EDA board (Arrow SocKit) software library + * I/O functions for communication with FPGA processes + * via memory mapping + */ + +#include "device.h" +#include "config.h" +#include "fpga.h" +#include "logger.h" + +#include +#include +#include +#include +#include +#include +#include + +#define PORT 8877 +#define NB_READOUTS_PER_FILE 10000 + +char g_state = 1; + +#include +void handle_sigint(int sig) { + log_printf("Caught signal %d\n", sig); + log_flush(); + g_state = 0; +} + +int device_init_mmap(Device_t *ctx); +int device_init_fpga(Device_t *ctx); +int device_init_hrsc(Device_t *ctx); + +int device_init_run(Device_t *ctx, uint32_t run); +void device_daq_write_dif(Device_t *ctx, int slot); +int device_daq_send_dif(Device_t *ctx, int slot); + +void give_file_to_server(char *filename, int sock); + +Device_t *new_device() { + Device_t *ctx = (Device_t *)calloc(1, sizeof(Device_t)); + signal(SIGINT, handle_sigint); + + strcpy(ctx->cfg_mode, "csv"); + + // fixed-location, temporary, line-buffered log file + log_init(); + g_state = 1; + + return ctx; +} + +void device_free(Device_t *ctx) { + free(ctx->ip_addr); + + // if (ctx->sock_cp != 0) { + // close(ctx->sock_cp); + // } + // if (ctx->sock_ctl != 0) { + // close(ctx->sock_ctl); + // } + + if (ctx->mem_fd != 0) { + munmap_lw_h2f(ctx->mem_fd); + munmap_h2f(ctx->mem_fd); + close(ctx->mem_fd); + } + + if (ctx->daq_file != 0) { + fclose(ctx->daq_file); + } + + for (int i = 0; i < NB_RFM; i++) { + free(ctx->task[i].addr); + if (ctx->task[i].sck != 0) { + close(ctx->task[i].sck); + } + } + + free(ctx); + ctx = NULL; +} + +int device_boot_rfm(Device_t *ctx, uint8_t dif, int slot, uint32_t rshaper, + uint32_t trig) { + ctx->rfm_on |= (1 << slot); + ctx->task[slot].dif = dif; + ctx->task[slot].rshaper = rshaper; + + if (ctx->rshaper != 0 && ctx->rshaper != rshaper) { + log_printf("invalid rshaper value: device=%d, config=%d\n", ctx->rshaper, + rshaper); + log_flush(); + return -1; + } + ctx->rshaper = rshaper; + ctx->trig_mode = trig; + return 0; +} + +int device_configure_dif(Device_t *ctx, uint8_t dif, const char *addr, + int port) { + strcpy(ctx->cfg_mode, "db"); + for (int i = 0; i < NB_RFM; i++) { + if (ctx->task[i].dif != dif) { + continue; + } + free(ctx->task[i].addr); + ctx->task[i].addr = strdup(addr); + ctx->task[i].port = port; + return 0; + } + return 1; +} + +int device_configure(Device_t *ctx, uint32_t thresh, uint32_t rshaper, + uint32_t rfm, const char *ip, int run) { + log_printf("device configuration from %s...\n", ctx->cfg_mode); + log_flush(); + + ctx->thresh_delta = thresh; + ctx->rshaper = rshaper; + ctx->rfm_on = rfm; + ctx->ip_addr = strdup(ip); + ctx->run_cnt = run; + + // copy base settings files from clrtodaq0 (using ssh keys) + char command[128]; + sprintf( + command, + "scp -P 1122 -r mim@193.48.81.203:/mim/soft/eda/config_base /dev/shm/"); + + int err = 0; + err = system(command); + if (err != 0) { + log_printf("could not copy base settings from clrtodaq\n"); + return err; + } + + // load files to tables + // single-HR configuration file + FILE *conf_base_file = fopen("/dev/shm/config_base/conf_base.csv", "r"); + if (!conf_base_file) + return -1; + if (HRSC_read_conf_singl(conf_base_file, 0) < 0) { + fclose(conf_base_file); + return -1; + } + fclose(conf_base_file); + + // floor thresholds + FILE *dac_floor_file = fopen("/dev/shm/config_base/dac_floor_4rfm.csv", "r"); + if (!dac_floor_file) + return -1; + if (read_th_offset(dac_floor_file, ctx->dac_floor_table) < 0) { + fclose(dac_floor_file); + return -1; + } + fclose(dac_floor_file); + + // preamplifier gains + FILE *pa_gain_file = fopen("/dev/shm/config_base/pa_gain_4rfm.csv", "r"); + if (!pa_gain_file) + return -1; + if (read_pa_gain(pa_gain_file, ctx->pa_gain_table) < 0) { + fclose(pa_gain_file); + return -1; + } + fclose(pa_gain_file); + + // masks + FILE *mask_file = fopen("/dev/shm/config_base/mask_4rfm.csv", "r"); + if (!mask_file) + return -1; + if (read_mask(mask_file, ctx->mask_table) < 0) { + fclose(mask_file); + return -1; + } + fclose(mask_file); + + return 0; +} + +int device_initialize(Device_t *ctx) { + int err = 0; + + // FPGA-HPS memory mapping------------------------------------------- + err = device_init_mmap(ctx); + if (err != 0) { + log_printf("could not initialize mmap\n"); + log_flush(); + return -1; + } + + // Init FPGA--------------------------------------------------------- + err = device_init_fpga(ctx); + if (err != 0) { + log_printf("could not initialize fpga\n"); + log_flush(); + return -1; + } + + // HR configuration-------------------------------------------------- + err = device_init_hrsc(ctx); + if (err != 0) { + log_printf("could not initialize hrsc\n"); + log_flush(); + return -1; + } + + // initialize DIM<->EDA data sockets. + err = device_init_scks(ctx); + if (err != 0) { + log_printf("could not initialize scks\n"); + log_flush(); + return -1; + } + + return 0; +} + +int device_init_scks(Device_t *ctx) { + for (int i = 0; i < NB_RFM; i++) { + if (ctx->task[i].dif == 0) { + continue; + } + log_printf( + "initialize DIM<->RFM data socket rfm=%d, slot=%d, addr=%s:%d...\n", + ctx->task[i].dif, i, ctx->task[i].addr, ctx->task[i].port); + log_flush(); + + struct sockaddr_in addr; + if ((ctx->task[i].sck = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + log_printf("could not create socket for rfm=%d, slot=%d\n", + ctx->task[i].dif, i); + log_flush(); + return -1; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(ctx->task[i].port); + + // Convert IPv4 and IPv6 addresses from text to binary form + if (inet_pton(AF_INET, ctx->task[i].addr, &addr.sin_addr) <= 0) { + log_printf("could not convert socket-addr for rfm=%d, slot=%d\n", + ctx->task[i].dif, i); + log_flush(); + return -1; + } + + if (connect(ctx->task[i].sck, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + log_printf("could not connect socket for rfm=%d, slot=%d\n", + ctx->task[i].dif, i); + log_flush(); + return -1; + } + } + + return 0; +} + +int device_init_mmap(Device_t *ctx) { + ctx->mem_fd = 0; + if ((ctx->mem_fd = open("/dev/mem", (O_RDWR | O_SYNC))) == -1) { + log_printf("ERROR: could not open \"/dev/mem\"...\n"); + log_flush(); + ctx->mem_fd = 0; + return -1; + } + // lightweight HPS to FPGA bus + if (!mmap_lw_h2f(ctx->mem_fd)) { + log_printf("could not mmap lw HPS to FPGA bus\n"); + log_flush(); + close(ctx->mem_fd); + ctx->mem_fd = 0; + return -1; + } + // HPS to FPGA bus + if (!mmap_h2f(ctx->mem_fd)) { + log_printf("could not mmap HPS to FPGA bus\n"); + log_flush(); + munmap_lw_h2f(ctx->mem_fd); + close(ctx->mem_fd); + ctx->mem_fd = 0; + return -1; + } + + return 0; +} + +int device_init_fpga(Device_t *ctx) { + // reset fpga and set clock + SYNC_reset_fpga(); + usleep(2); + // make sure the pll is locked + int cnt_poll = 0; + while ((!SYNC_pll_lck()) && (cnt_poll < 100)) { + usleep(10000); + cnt_poll++; + } + if (cnt_poll >= 100) { + log_printf("the PLL is not locked\n"); + log_flush(); + return -1; + } + log_printf("the PLL is locked\n"); + log_printf("pll lock=%d\n", SYNC_pll_lck()); + log_flush(); + + // activate RFMs + int rfm_index; + for (rfm_index = 0; rfm_index < NB_RFM; rfm_index++) { + if (((ctx->rfm_on >> rfm_index) & 1) == 1) { + RFM_on(rfm_index); + RFM_enable(rfm_index); + } + } + sleep(1); + log_printf("control pio=%lx\n", PIO_ctrl_get()); + log_flush(); + + log_printf("trigger mode: %d\n", ctx->trig_mode); + log_flush(); + if (ctx->trig_mode == 0) { + SYNC_select_command_dcc(); + SYNC_enable_dcc_busy(); + SYNC_enable_dcc_ramfull(); + } + if (ctx->trig_mode == 1) { + SYNC_select_command_soft(); + } + + return 0; +} + +int device_init_hrsc_from_csv(Device_t *ctx); +int device_init_hrsc_from_db(Device_t *ctx); + +int device_init_hrsc(Device_t *ctx) { + log_printf("Hardroc configuration from %s...\n", ctx->cfg_mode); + log_flush(); + + // disable trig_out output pin (RFM v1 coupling problem) + HRSC_set_bit(0, 854, 0); + + HRSC_set_shaper_resis(0, ctx->rshaper); + HRSC_set_shaper_capa(0, 3); + + // set chip ids + for (alt_u32 hr_addr = 0; hr_addr < 8; hr_addr++) { + HRSC_set_chip_id(hr_addr, hr_addr + 1); + } + + if (strcmp(ctx->cfg_mode, "db") == 0) { + return device_init_hrsc_from_db(ctx); + } + + if (strcmp(ctx->cfg_mode, "csv") == 0) { + return device_init_hrsc_from_csv(ctx); + } + + log_printf("invalid configuration mode %q\n", ctx->cfg_mode); + log_flush(); + return -1; +} + +int device_init_hrsc_from_db(Device_t *ctx) { + alt_u32 hr_addr, chan; + // for each active RFM, tune the configuration and send it + for (int rfm_index = 0; rfm_index < NB_RFM; rfm_index++) { + if (((ctx->rfm_on >> rfm_index) & 1) == 1) { + // set mask + alt_u32 mask; + for (hr_addr = 0; hr_addr < 8; hr_addr++) { + for (chan = 0; chan < 64; chan++) { + mask = ctx->mask_table[64 * (NB_HR * rfm_index + hr_addr) + chan]; + HRSC_set_mask(hr_addr, chan, mask); + } + } + // set DAC thresholds + alt_u32 th0, th1, th2; + for (hr_addr = 0; hr_addr < 8; hr_addr++) { + th0 = ctx->dac_floor_table[3 * (NB_HR * rfm_index + hr_addr) + 0]; + th1 = ctx->dac_floor_table[3 * (NB_HR * rfm_index + hr_addr) + 1]; + th2 = ctx->dac_floor_table[3 * (NB_HR * rfm_index + hr_addr) + 2]; + HRSC_set_DAC0(hr_addr, th0); + HRSC_set_DAC1(hr_addr, th1); + HRSC_set_DAC2(hr_addr, th2); + } + // set preamplifier gain + alt_u32 pa_gain; + for (hr_addr = 0; hr_addr < 8; hr_addr++) { + for (chan = 0; chan < 64; chan++) { + pa_gain = + ctx->pa_gain_table[64 * (NB_HR * rfm_index + hr_addr) + chan]; + HRSC_set_preamp(hr_addr, chan, pa_gain); + } + } + // send to HRs + if (HRSC_set_config(rfm_index) < 0) { + PRINT_config(stderr, rfm_index); + return -1; + } + log_printf("Hardroc configuration done (rfm=%d, slot=%d)\n", + ctx->task[rfm_index].dif, rfm_index); + log_flush(); + if (HRSC_reset_read_registers(rfm_index) < 0) { + PRINT_config(stderr, rfm_index); + return -1; + } + } + } + + log_printf("read register reset done\n"); + log_flush(); + sleep(1); // let DACs stabilize + log_printf("Hardroc configuration from %s... [done]\n", ctx->cfg_mode); + log_flush(); + return 0; +} + +int device_init_hrsc_from_csv(Device_t *ctx) { + int err = 0; + + HRSC_copy_conf(0, 1); + HRSC_copy_conf(0, 2); + HRSC_copy_conf(0, 3); + HRSC_copy_conf(0, 4); + HRSC_copy_conf(0, 5); + HRSC_copy_conf(0, 6); + HRSC_copy_conf(0, 7); + + // prepare config file (for history) + char sc_filename[128]; + sprintf(sc_filename, "/home/root/run/hr_sc_%03d.csv", ctx->run_cnt); + FILE *sc_file = fopen(sc_filename, "w"); + if (!sc_file) { + log_printf("could not open file %s\n", sc_filename); + log_flush(); + return -1; + } + + alt_u32 hr_addr, chan; + + // for each active RFM, tune the configuration and send it + for (int rfm_index = 0; rfm_index < NB_RFM; rfm_index++) { + if (((ctx->rfm_on >> rfm_index) & 1) == 1) { + // set mask + alt_u32 mask; + for (hr_addr = 0; hr_addr < 8; hr_addr++) { + for (chan = 0; chan < 64; chan++) { + mask = ctx->mask_table[64 * (NB_HR * rfm_index + hr_addr) + chan]; + log_printf("%u %u %u\n", (uint32_t)hr_addr, (uint32_t)chan, + (uint32_t)mask); + log_flush(); + HRSC_set_mask(hr_addr, chan, mask); + } + } + // set DAC thresholds + log_printf("HR thresh0 thresh1 thresh2\n"); + log_flush(); + alt_u32 th0, th1, th2; + for (hr_addr = 0; hr_addr < 8; hr_addr++) { + th0 = ctx->dac_floor_table[3 * (NB_HR * rfm_index + hr_addr)] + + ctx->thresh_delta; + th1 = ctx->dac_floor_table[3 * (NB_HR * rfm_index + hr_addr) + 1] + + ctx->thresh_delta; + th2 = ctx->dac_floor_table[3 * (NB_HR * rfm_index + hr_addr) + 2] + + ctx->thresh_delta; + log_printf("%u %u %u %u\n", (uint32_t)hr_addr, + (uint32_t)th0, (uint32_t)th1, (uint32_t)th2); + log_flush(); + HRSC_set_DAC0(hr_addr, th0); + HRSC_set_DAC1(hr_addr, th1); + HRSC_set_DAC2(hr_addr, th2); + } + // set preamplifier gain + log_printf("HR chan pa_gain\n"); + log_flush(); + alt_u32 pa_gain; + for (hr_addr = 0; hr_addr < 8; hr_addr++) { + for (chan = 0; chan < 64; chan++) { + pa_gain = + ctx->pa_gain_table[64 * (NB_HR * rfm_index + hr_addr) + chan]; + log_printf("%u %u %u\n", (uint32_t)hr_addr, (uint32_t)chan, + (uint32_t)pa_gain); + log_flush(); + HRSC_set_preamp(hr_addr, chan, pa_gain); + } + } + // send to HRs + if (HRSC_set_config(rfm_index) < 0) { + PRINT_config(stderr, rfm_index); + return -1; + } + log_printf("Hardroc configuration done\n"); + log_flush(); + if (HRSC_reset_read_registers(rfm_index) < 0) { + PRINT_config(stderr, rfm_index); + return -1; + } + fprintf(sc_file, "#RFM_INDEX= %d ------------------------\n", rfm_index); + HRSC_write_conf_mult(sc_file); + } + } + fclose(sc_file); + char command[256]; + sprintf(command, + "scp -P 1122 %s mim@193.48.81.203:/mim/soft/eda/config_history/", + sc_filename); + err = system(command); + if (err != 0) { + log_printf("could not send config to history store: err=%d\n", err); + log_flush(); + return err; + } + + log_printf("read register reset done\n"); + log_flush(); + sleep(1); // let DACs stabilize + + log_printf("Hardroc configuration from %s... [done]\n", ctx->cfg_mode); + log_flush(); + return 0; +} + +int device_start_run_noise(Device_t *ctx, uint32_t run); +int device_start_run_dcc(Device_t *ctx, uint32_t run); + +int device_start(Device_t *ctx, uint32_t run) { + int err = 0; + + // init run---------------------------------------------------------- + err = device_init_run(ctx, run); + if (err != 0) { + return err; + } + + if (ctx->trig_mode == 0) { + return device_start_run_dcc(ctx, run); + } + if (ctx->trig_mode == 1) { + return device_start_run_noise(ctx, run); + } + log_printf("unknown trig-mode: %d\n", ctx->trig_mode); + log_flush(); + return -1; +} + +int device_start_run_dcc(Device_t *ctx, uint32_t run) { + log_printf("start-run(%d) mode=dcc...\n", run); + log_flush(); + // wait for reset BCID + log_printf("waiting for reset_BCID command\n"); + log_flush(); + // send(ctx->sock_ctl, "eda-ready", 9, 0); + + int dcc_cmd = 0xE; + while (dcc_cmd != CMD_RESET_BCID) { + while (SYNC_dcc_cmd_mem() == dcc_cmd) { + if (g_state == 0) + break; + } + dcc_cmd = SYNC_dcc_cmd_mem(); + // log_printf("sDCC command = %d\n",dcc_cmd); + if (g_state == 0) + break; + } + if (g_state == 0) { + return -1; + } + log_printf("SYNC_state()=%d\n", SYNC_state()); + log_printf("reset_BCID done\n"); + log_flush(); + + CNT_reset(); + CNT_start(); + for (int rfm_index = 0; rfm_index < NB_RFM; rfm_index++) { + if (((ctx->rfm_on >> rfm_index) & 1) == 1) { + DAQ_fifo_init(rfm_index); + } + } + + ctx->cycle_id = 0; + SYNC_fifo_arming(); + + // for (int i = 0; i < NB_RFM; i++) { + // if (((ctx->rfm_on >> i) & 1) == 0) { + // continue; + // } + // err = pthread_create(&ctx->task[i].id, NULL, device_loop, (void + // *)ctx); if (err != 0) { + // log_printf("could not create worker for RFM=%d: err=%d\n", i, err); + // log_flush(); + // return err; + // } + // } + + // err = pthread_create(&ctx->task[0].id, NULL, device_loop, (void *)ctx); + // if (err != 0) { + // log_printf("could not create worker: err=%d\n", err); + // log_flush(); + // return err; + // } + + return 0; +} + +int device_start_run_noise(Device_t *ctx, uint32_t run) { + log_printf("start-run(%d) mode=noise...\n", run); + log_flush(); + for (int i = 0; i < NB_RFM; i++) { + if (((ctx->rfm_on >> i) & 1) == 1) { + DAQ_fifo_init(i); + } + } + + CNT_reset(); + SYNC_reset_bcid(); + SYNC_start_acq(); + log_printf("SYNC_state()=%d\n", SYNC_state()); + log_printf("reset_BCID done\n"); + log_flush(); + + ctx->cycle_id = 0; + SYNC_fifo_arming(); + + return 0; +} + +int device_init_run(Device_t *ctx, uint32_t run) { + // save run-dependant settings + ctx->run_cnt = run; + log_printf("thresh_delta=%lu, Rshaper=%lu, rfm_on[3:0]=%lu\n", + ctx->thresh_delta, ctx->rshaper, ctx->rfm_on); + log_flush(); + // char settings_filename[128]; + // sprintf(settings_filename, "/home/root/run/settings_%03d.csv", + // ctx->run_cnt); FILE *settings_file = fopen(settings_filename, "w"); if + // (!settings_file) { + // log_printf("could not open file %s\n", settings_filename); + // log_flush(); + // return -1; + // } + // fprintf( + // settings_file, + // "thresh_delta=%lu; Rshaper=%lu; rfm_on[3:0]=%lu; ip_addr=%s; + // run_id=%d", ctx->thresh_delta, ctx->rshaper, ctx->rfm_on, + // ctx->ip_addr, ctx->run_cnt); + // fclose(settings_file); + // give_file_to_server(settings_filename, ctx->sock_cp); + + log_printf("-----------------RUN NB %d-----------------\n", ctx->run_cnt); + log_flush(); + + // FIXME(sbinet): replace with a pair of open_memstream(3). + // prepare run file + sprintf(ctx->daq_filename, "/dev/shm/eda_%03d.000.raw", + ctx->run_cnt); // use tmpfs for daq (to reduce writings on µSD flash + // mem) + ctx->daq_file = fopen(ctx->daq_filename, "w"); + if (!ctx->daq_file) { + log_printf("unable to open file %s\n", ctx->daq_filename); + log_flush(); + return -1; + } + // init run counters + ctx->cycle_id = 0; + + SYNC_reset_hr(); + + return 0; +} + +void device_loop_noise(Device_t *ctx); +void device_loop_dcc(Device_t *ctx); + +void device_loop(Device_t *ctx) { + if (ctx->trig_mode == 0) { + device_loop_dcc(ctx); + return; + } + if (ctx->trig_mode == 1) { + device_loop_noise(ctx); + return; + } +} + +void device_loop_dcc(Device_t *ctx) { + int err = 0; + // int file_cnt = 0; + //----------- run loop ----------------- + while (g_state == 1) { + DAQ_reset_buffer(); + // wait until new readout is started + log_printf("trigger %07lu\n\tacq\n", ctx->cycle_id); + log_flush(); + while ((SYNC_fpga_ro() == 0) && (g_state == 1)) { + ; + } + if (g_state == 0) { + break; + } + + log_printf("\treadout\n"); + log_flush(); + // wait until readout is done + while ((SYNC_fifo_ready() == 0) && (g_state == 1)) { + ; + } + if (g_state == 0) { + break; + } + + // read hardroc data + log_printf("\tbuffering\n"); + log_flush(); + for (int rfm_index = 0; rfm_index < NB_RFM; rfm_index++) { + if (((ctx->rfm_on >> rfm_index) & 1) == 0) { + continue; + } + log_printf("\t\trfm %d\n", rfm_index); + log_flush(); + device_daq_write_dif(ctx, rfm_index); + if (g_state == 0) { + break; + } + } + SYNC_fifo_ack(); + + // write data file + log_printf("\tfwrite\n"); + log_flush(); + DAQ_write_buffer(ctx->daq_file); + log_printf("\tdone\n"); + log_flush(); + for (int i = 0; i < NB_RFM; i++) { + if (((ctx->rfm_on >> i) & 1) == 0) { + continue; + } + err = device_daq_send_dif(ctx, i); + if (err != 0) { + log_printf("\tcould not send dif data RFM=%d, slot=%d: err=%d\n", + ctx->task[i].dif, i, err); + ctx->task[i].rc = err; + } + } + + ctx->cycle_id++; + // if ((ctx->cycle_id % NB_READOUTS_PER_FILE) == 0) { + // fclose(ctx->daq_file); + // give_file_to_server(ctx->daq_filename, ctx->sock_cp); + // // prepare new file + // memset(ctx->daq_filename, 0, 128); + // file_cnt++; + // sprintf(ctx->daq_filename, "/dev/shm/eda_%03d.%03d.raw", + // ctx->run_cnt, + // file_cnt); + // ctx->daq_file = fopen(ctx->daq_filename, "w"); + // } + } + + return; +} + +void device_loop_noise(Device_t *ctx) { + int err = 0; + while (g_state == 1) { + DAQ_reset_buffer(); + log_printf("trigger %07lu\n\tacq\n", ctx->cycle_id); + log_flush(); + // wait for ramfull + while ((SYNC_ramfull() == 0) && (g_state == 1)) { + ; + } + if (g_state == 0) + break; + log_printf("\tramfull\n"); + log_flush(); + SYNC_ramfull_ext(); + // wait until data is ready + while ((SYNC_fifo_ready() == 0) && (g_state == 1)) { + ; + } + if (g_state == 0) { + break; + } + // read hardroc data + log_printf("\tbuffering\n"); + log_flush(); + for (int rfm_index = 0; rfm_index < NB_RFM; rfm_index++) { + if (((ctx->rfm_on >> rfm_index) & 1) == 0) { + continue; + } + log_printf("\t\trfm %d\n", rfm_index); + log_flush(); + device_daq_write_dif(ctx, rfm_index); + if (g_state == 0) { + break; + } + } + SYNC_fifo_ack(); + log_printf("\tdone\n"); + log_flush(); + for (int i = 0; i < NB_RFM; i++) { + if (((ctx->rfm_on >> i) & 1) == 0) { + continue; + } + err = device_daq_send_dif(ctx, i); + if (err != 0) { + log_printf("\tcould not send dif data RFM=%d, slot=%d: err=%d\n", + ctx->task[i].dif, i, err); + ctx->task[i].rc = err; + } + } + + SYNC_start_acq(); + ctx->cycle_id++; + } +} + +int device_daq_send_dif(Device_t *ctx, int slot) { + int err = 0; + int ret = 0; + int sck = ctx->task[slot].sck; + uint8_t buf[8] = {'H', 'D', 'R', 0, 0, 0, 0, 0}; + uint8_t *data = ctx->task[slot].beg; + uint32_t size = ctx->task[slot].end - ctx->task[slot].beg; + + buf[4 + 0] = (uint8_t)(size); + buf[4 + 1] = (uint8_t)(size >> 8); + buf[4 + 2] = (uint8_t)(size >> 16); + buf[4 + 3] = (uint8_t)(size >> 24); + + err = send(sck, buf, 8, 0); + if (err == -1) { + log_printf("could not send DIF header (rfm=%d, slot=%d): err=%d\n", + ctx->task[slot].dif, slot, err); + log_flush(); + return err; + } + + ret = recv(sck, buf, 4, 0); + if (ret != 4 || (strcmp((const char *)buf, "ACK") != 0)) { + log_printf("could not recv HDR-ACK (rfm=%d, slot=%d): sz=%d buf=%s\n", + ctx->task[slot].dif, slot, ret, (const char *)buf); + log_flush(); + return 1; + } + + if (size == 0) { + return 0; + } + + err = send(sck, data, size, 0); + if (err == -1) { + log_printf("could not send DIF data (rfm=%d, slot=%d): err=%d\n", + ctx->task[slot].dif, slot, err); + log_flush(); + } + + ret = recv(sck, buf, 4, 0); + if (ret != 4 || (strcmp((const char *)buf, "ACK") != 0)) { + log_printf( + "could not recv DIF data (rfm=%d, slot=%d) ACK: size=%d, buf=%s\n", + ctx->task[slot].dif, slot, ret, (const char *)buf); + log_flush(); + return 1; + } + + return 0; +} + +int device_stop(Device_t *ctx) { + if (ctx->trig_mode == 0) { + CNT_stop(); + } + if (ctx->trig_mode == 1) { + SYNC_stop_acq(); + CNT_stop(); + } + CNT_reset(); + SYNC_reset_fpga(); + SYNC_reset_hr(); + + // close current daq file + fclose(ctx->daq_file); + ctx->daq_file = NULL; + + // give_file_to_server(ctx->daq_filename, ctx->sock_cp); + return 0; +} + +void device_stop_loop(Device_t *ctx) { + g_state = 0; // FIXME(sbinet): r/w race. +} + +void give_file_to_server(char *filename, int sock) { + if (sock == 0) { + return; + } + log_printf("send copy request to eda-srv\n"); + log_flush(); + alt_u32 length; + alt_u8 length_litend[4] = {0}; + char sock_read_buf[4] = {0}; + int valread; + // send lenght of filename (uint32 little endian) to server + length = strlen(filename); + length_litend[0] = length; // length < 128 < 256 + send(sock, length_litend, 4, 0); + // send filename to server + send(sock, filename, length, 0); + // wait server ack + valread = read(sock, sock_read_buf, 3); + if ((valread != 3) || (strcmp(sock_read_buf, "ACK") != 0)) { + log_printf("instead of ACK, received :%s\n", sock_read_buf); + log_flush(); + } + return; +} diff --git a/eda/device.h b/eda/device.h new file mode 100644 index 0000000..d3b49e6 --- /dev/null +++ b/eda/device.h @@ -0,0 +1,91 @@ +#ifndef EDA_DEVICE_H +#define EDA_DEVICE_H 1 + +/* + * Copyright 2020 Baptiste Joly + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +/** + * EDA board (Arrow SocKit) software library + * I/O functions for communication with FPGA processes + * via memory mapping + */ + +#include "fpga.h" +#include + +typedef struct Device { + // run-dependant settings + alt_u32 thresh_delta; + alt_u32 rshaper; + alt_u32 rfm_on; + + char *ip_addr; + int run_cnt; + + int trig_mode; // 0: dcc, 1:soft + // baseline settings + char cfg_mode[4]; // 'db' or 'csv' + alt_u32 dac_floor_table[NB_RFM * NB_HR * 3]; + alt_u32 pa_gain_table[NB_RFM * NB_HR * 64]; + alt_u32 mask_table[NB_RFM * NB_HR * 64]; + + int mem_fd; + + char daq_filename[128]; + FILE *daq_file; + alt_u32 cycle_id; // run counters + + struct { + uint8_t dif; + uint32_t rshaper; + char *addr; + int port; + int sck; + + uint8_t *beg; + uint8_t *end; + alt_u32 bcid48_offset; + + int rc; + } task[NB_RFM]; // run-loop status +} Device_t; + +Device_t *new_device(); + +int device_boot_rfm(Device_t *ctx, uint8_t dif, int slot, uint32_t rshaper, + uint32_t trig); +int device_configure_dif(Device_t *ctx, uint8_t dif, const char *addr, + int port); +int device_configure(Device_t *ctx, uint32_t thresh, uint32_t rshaper, + uint32_t rfm, const char *ip, int run); +int device_initialize(Device_t *ctx); +int device_start(Device_t *ctx, uint32_t run); +void device_loop(Device_t *ctx); +int device_stop(Device_t *ctx); +void device_stop_loop(Device_t *ctx); + +void device_free(Device_t *ctx); + +int device_init_mmap(Device_t *ctx); +int device_init_fpga(Device_t *ctx); +int device_init_hrsc(Device_t *ctx); +int device_init_scks(Device_t *ctx); + +#endif // !EDA_DEVICE_H diff --git a/eda/fpga.c b/eda/fpga.c new file mode 100644 index 0000000..9f87218 --- /dev/null +++ b/eda/fpga.c @@ -0,0 +1,1608 @@ +// Copyright 2021 The tomuvol-daq Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "device.h" +#include "fpga.h" + +// static variables, with file-scope +// are persistent between function calls and +// can be modified by all the functions below + +static void *virtual_lw_h2f; +static void *virtual_h2f; +static int i2c_file; + +static alt_u32 *p_pio_state; +static alt_u32 *p_pio_irq_bus; +static alt_u32 *p_pio_irq_mask; +// static alt_u32 *p_pio_irq_context; +static alt_u32 *p_pio_ctrl; +static alt_u32 *p_pio_pulser; + +static alt_u32 *p_ram_sc[4]; +static alt_u32 *p_pio_sc_check[4]; + +static alt_u32 *p_fifo_daq[4]; +static alt_u32 *p_fifo_daq_csr[4]; + +static alt_u32 *p_pio_cnt_hit0[4]; +static alt_u32 *p_pio_cnt_hit1[4]; +static alt_u32 *p_pio_cnt_trig; +static alt_u32 *p_pio_cnt48_msb; +static alt_u32 *p_pio_cnt48_lsb; +static alt_u32 *p_pio_cnt24; + +static alt_u8 this_eda_id = 1; // 0 to 7 +static alt_u32 daq_cycle_id[4] = {0, 0, 0, 0}; + +static uint8_t hr_config_buffer[NB_HR * NB_BYTES_CFG_HR + 4] = {0}; +static uint8_t *hr_config_data = &(hr_config_buffer[4]); + +static uint8_t hr_data_buffer[NB_RFM * (26 + NB_HR * (2 + 128 * 20))] = {0}; +static uint8_t *hr_data_cursor = hr_data_buffer; + +#define MASK_1_BIT 0x1 + +/************** memory-mapping *****************/ + +int mmap_lw_h2f(int fd) { + virtual_lw_h2f = mmap(NULL, LW_H2F_SPAN, (PROT_READ | PROT_WRITE), MAP_SHARED, + fd, LW_H2F_BASE); + if (virtual_lw_h2f == MAP_FAILED) { + fprintf(stderr, + "ERROR: mmap() failed for lightweight HPS to FPGA bridge...\n"); + return (-1); + } + p_pio_state = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_STATE_IN); + p_pio_irq_bus = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_INTERRUPTS_BUS); + p_pio_irq_mask = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_INTERRUPTS_MASK); + p_pio_ctrl = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CTRL_OUT); + p_pio_pulser = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_PULSER); + + p_ram_sc[0] = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_RAM_SC_RFM0); + p_ram_sc[1] = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_RAM_SC_RFM1); + p_ram_sc[2] = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_RAM_SC_RFM2); + p_ram_sc[3] = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_RAM_SC_RFM3); + + p_pio_sc_check[0] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_SC_CHECK_RFM0); + p_pio_sc_check[1] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_SC_CHECK_RFM1); + p_pio_sc_check[2] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_SC_CHECK_RFM2); + p_pio_sc_check[3] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_SC_CHECK_RFM3); + + p_pio_cnt_hit0[0] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT_HIT0_RFM0); + p_pio_cnt_hit0[1] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT_HIT0_RFM1); + p_pio_cnt_hit0[2] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT_HIT0_RFM2); + p_pio_cnt_hit0[3] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT_HIT0_RFM3); + + p_pio_cnt_hit1[0] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT_HIT1_RFM0); + p_pio_cnt_hit1[1] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT_HIT1_RFM1); + p_pio_cnt_hit1[2] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT_HIT1_RFM2); + p_pio_cnt_hit1[3] = + (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT_HIT1_RFM3); + + p_pio_cnt_trig = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT_TRIG); + p_pio_cnt48_msb = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT48_MSB); + p_pio_cnt48_lsb = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT48_LSB); + p_pio_cnt24 = (alt_u32 *)((alt_u32)virtual_lw_h2f + LW_H2F_PIO_CNT24); + return (1); +} + +int mmap_h2f(int fd) { + virtual_h2f = + mmap(NULL, H2F_SPAN, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, H2F_BASE); + if (virtual_h2f == MAP_FAILED) { + fprintf(stderr, "ERROR: mmap() failed for HPS to FPGA bridge...\n"); + return (-1); + } + p_fifo_daq[0] = (alt_u32 *)((alt_u32)virtual_h2f + H2F_FIFO_DAQ_RFM0); + p_fifo_daq[1] = (alt_u32 *)((alt_u32)virtual_h2f + H2F_FIFO_DAQ_RFM1); + p_fifo_daq[2] = (alt_u32 *)((alt_u32)virtual_h2f + H2F_FIFO_DAQ_RFM2); + p_fifo_daq[3] = (alt_u32 *)((alt_u32)virtual_h2f + H2F_FIFO_DAQ_RFM3); + + p_fifo_daq_csr[0] = (alt_u32 *)((alt_u32)virtual_h2f + H2F_FIFO_DAQ_CSR_RFM0); + p_fifo_daq_csr[1] = (alt_u32 *)((alt_u32)virtual_h2f + H2F_FIFO_DAQ_CSR_RFM1); + p_fifo_daq_csr[2] = (alt_u32 *)((alt_u32)virtual_h2f + H2F_FIFO_DAQ_CSR_RFM2); + p_fifo_daq_csr[3] = (alt_u32 *)((alt_u32)virtual_h2f + H2F_FIFO_DAQ_CSR_RFM3); + + return (1); +} + +int munmap_lw_h2f(int fd) { + if (munmap(virtual_lw_h2f, LW_H2F_SPAN) != 0) { + fprintf(stderr, "ERROR: munmap() failed for lw_h2f...\n"); + return (-1); + } + return (1); +} + +int munmap_h2f(int fd) { + if (munmap(virtual_h2f, H2F_SPAN) != 0) { + fprintf(stderr, "ERROR: munmap() failed for h2f...\n"); + return (-1); + } + return (1); +} + +// binary write functions -- big endian, unsigned, 8/16/32 bits + +ssize_t fwrite_u8(alt_u8 byte, FILE *output) { + return fwrite(&byte, 1, 1, output); +} + +ssize_t fwrite_u16(alt_u16 word16, FILE *output) { + alt_u16 bigendian16 = htons(word16); + return fwrite(&bigendian16, 2, 1, output); +} + +ssize_t fwrite_u24(alt_u32 word32, FILE *output) { + alt_u8 word8 = (word32 >> 16) & 0xFF; + alt_u16 word16 = word32 & 0xFFFF; + ssize_t ret = fwrite_u8(word8, output); + ret += fwrite_u16(word16, output); + return ret; +} + +ssize_t fwrite_u32(alt_u32 word32, FILE *output) { + alt_u32 bigendian32 = htonl(word32); + return fwrite(&bigendian32, 4, 1, output); +} + +/************** pio general *****************/ + +void PIO_set_defaults() { *p_pio_ctrl = 0; } + +alt_u32 PIO_state_get() { return (*p_pio_state); } + +alt_u32 PIO_ctrl_get() { return (*p_pio_ctrl); } + +// static irqreturn_t PIO_handle_interrupts(int irq, void *dev_id) +//{ +// if (irq != PIO_IRQ) +// return IRQ_NONE; +///* Store the value of *p_pio_irq_bus in *p_pio_irq_context. */ +//*p_pio_irq_context = *p_pio_irq_bus; +///* mask the bit that caused the interrupt, unmask the others */ +//*p_pio_irq_mask = ~(*p_pio_irq_bus) & 0xf; +// fprintf(stderr,"----interrupt, state="); +// switch(*irq_bus){ +// case IRQ_RST_BCID: +// fprintf(stderr,"reset_bcid\n"); +// break; +// case IRQ_ACQ: +// fprintf(stderr,"acq\n"); +// break; +// case IRQ_RO: +// fprintf(stderr,"readout\n"); +// break; +// case IRQ_FIFO_READY: +// fprintf(stderr,"fifo_ready\n"); +// break; +// default: +// fprintf(stderr,"illegal state\n"); +// break; +//} +// return IRQ_HANDLED; +//} + +// PIO_interrupts_init(){ +//*p_pio_irq_mask = 0xf; //enable all 4 bits +// request_irq(PIO_IRQ, PIO_handle_interrupts, +// 0, "state_machine_interrupts", NULL); +//} + +/************** i2c general *****************/ + +int I2C_init() { + if ((i2c_file = open("/dev/i2c-0", O_RDWR)) < 0) { + /* ERROR HANDLING: you can check errno to see what went wrong */ + perror("Failed to open the i2c bus of gsensor"); + return (-1); + } + return (1); +} + +void I2C_close() { close(i2c_file); } + +/********************** RFM on-off, enable *********************/ + +void RFM_on(int rfm) { + switch (rfm) { + case 0: + *p_pio_ctrl |= O_ON_OFF_RFM0; + return; + case 1: + *p_pio_ctrl |= O_ON_OFF_RFM1; + return; + case 2: + *p_pio_ctrl |= O_ON_OFF_RFM2; + return; + case 3: + *p_pio_ctrl |= O_ON_OFF_RFM3; + return; + default: + fprintf(stderr, "rfm_on: bad argument %d\n", rfm); + return; + } +}; + +void RFM_off(int rfm) { + switch (rfm) { + case 0: + *p_pio_ctrl &= ~O_ON_OFF_RFM0; + return; + case 1: + *p_pio_ctrl &= ~O_ON_OFF_RFM1; + return; + case 2: + *p_pio_ctrl &= ~O_ON_OFF_RFM2; + return; + case 3: + *p_pio_ctrl &= ~O_ON_OFF_RFM3; + return; + default: + fprintf(stderr, "rfm_off: bad argument %d\n", rfm); + return; + } +}; + +void RFM_enable(int rfm) { + switch (rfm) { + case 0: + *p_pio_ctrl |= O_ENA_RFM0; + return; + case 1: + *p_pio_ctrl |= O_ENA_RFM1; + return; + case 2: + *p_pio_ctrl |= O_ENA_RFM2; + return; + case 3: + *p_pio_ctrl |= O_ENA_RFM3; + return; + default: + fprintf(stderr, "rfm_enable: bad argument %d\n", rfm); + return; + } +}; + +void RFM_disable(int rfm) { + switch (rfm) { + case 0: + *p_pio_ctrl &= ~O_ENA_RFM0; + return; + case 1: + *p_pio_ctrl &= ~O_ENA_RFM1; + return; + case 2: + *p_pio_ctrl &= ~O_ENA_RFM2; + return; + case 3: + *p_pio_ctrl &= ~O_ENA_RFM3; + return; + default: + fprintf(stderr, "rfm_disable: bad argument %d\n", rfm); + return; + } +}; + +/********************** monitoring *********************/ + +int MONIT_pwr_alert(int rfm) { + switch (rfm) { + case 0: + return ((*p_pio_state & O_ALERT_0) == O_ALERT_0); + case 1: + return ((*p_pio_state & O_ALERT_1) == O_ALERT_1); + case 2: + return ((*p_pio_state & O_ALERT_2) == O_ALERT_2); + case 3: + return ((*p_pio_state & O_ALERT_3) == O_ALERT_3); + default: + fprintf(stderr, "pwr_alert: bad argument %d\n", rfm); + return (-1); + } +} + +int MONIT_PTH_select() { + int addr = 0x76; + if (ioctl(i2c_file, I2C_SLAVE, addr) < 0) { + fprintf(stderr, + "Failed to acquire PTH bus access and/or talk to slave (with " + "address %d).\n", + addr); + return (-1); + } + return (1); +} + +int MONIT_PTH_read_id() { + // register add + alt_u8 addr = 0xD0; + // write register addr + if (write(i2c_file, &addr, 1) != 1) { + fprintf(stderr, "Failed to write register addr. to PTH.\n"); + return (-1); + } + // read id + alt_u8 id; + if (read(i2c_file, &id, 1) != 1) { + fprintf(stderr, "Failed to read PTH id.\n"); + return (-1); + } + return ((int)id); +} + +int MONIT_PT100_select() { + int addr = 0x44; + if (ioctl(i2c_file, I2C_SLAVE, addr) < 0) { + fprintf(stderr, + "Failed to acquire PT100 bus access and/or talk to slave (with " + "address %d).\n", + addr); + return (-1); + } + return (1); +} + +int MONIT_PT100_write_reg(alt_u8 reg_addr, alt_u8 value) { + // write register addr + alt_u8 frame[2]; + frame[0] = 0x40 | ((reg_addr << 2) & 0XC); + frame[1] = value; + if (write(i2c_file, frame, 2) != 2) { + fprintf(stderr, "Failed to write to PT100, reg addr=%x, value=%x\n", + reg_addr, value); + return (-1); + } + alt_u8 check; + frame[0] = 0x20 | ((reg_addr << 2) & 0XC); + if (write(i2c_file, frame, 1) != 1) { + fprintf(stderr, "Failed to access to PT100 reg @ addr=%x\n", reg_addr); + return (-1); + } + if (read(i2c_file, &check, 1) != 1) { + fprintf(stderr, "Failed to read PT100 reg @ addr=%x\n", reg_addr); + return (-1); + } + if (check != value) { + fprintf(stderr, "PT100: written and read value differ.\n"); + fprintf(stderr, "read value = %x\n", check); + return (-1); + } + return ((int)check); +} + +int MONIT_PWRMON_select(int rfm) { + int addr = 0x40 + (rfm & 0x3); + if (ioctl(i2c_file, I2C_SLAVE, addr) < 0) { + fprintf(stderr, + "Failed to acquire INA226 (RFM no %d) bus access and/or talk to " + "slave (with address %d).\n", + rfm, addr); + return (-1); + } + return (1); +} + +int MONIT_PWRMON_readreg(alt_u8 reg_addr) { + // write register addr + if (write(i2c_file, ®_addr, 1) != 1) { + fprintf(stderr, "Failed to write register addr. to INA226\n"); + return (-1); + } + // read reg + alt_u8 val[2]; + if (read(i2c_file, val, 2) != 2) { + fprintf(stderr, "Failed to read INA226 reg %x\n", reg_addr); + return (-1); + } + int result = val[0] * 0x100 + val[1]; + return (result); +} + +void MONIT_BBL_reset() { + *p_pio_ctrl |= O_BBL_RST; + usleep(1); + *p_pio_ctrl &= ~O_BBL_RST; + return; +} + +/*************** clocks and synchronization ************/ + +void SYNC_reset_fpga() { + *p_pio_ctrl = O_RESET; + fprintf(stderr, "Reset FPGA\n"); + usleep(1); + *p_pio_ctrl = 0x00000000; + usleep(1); +} + +void SYNC_reset_hr() { + *p_pio_ctrl |= O_RESET_HR; + // fprintf(stderr,"reset hardroc\n"); + usleep(1); + *p_pio_ctrl &= ~O_RESET_HR; + usleep(1); +} + +int SYNC_pll_lck() { return ((*p_pio_state & O_PLL_LCK) == O_PLL_LCK); } + +int SYNC_state() { return (((*p_pio_state) >> SHIFT_SYNCHRO_STATE) & 0xF); } + +void SYNC_select_command_soft() { *p_pio_ctrl |= O_SEL_CMD_SOURCE; } + +void SYNC_select_command_dcc() { *p_pio_ctrl &= ~O_SEL_CMD_SOURCE; } + +void SYNC_set_command(alt_u32 cmd) { + alt_u32 pio_val = *p_pio_ctrl; + pio_val &= ~(0xF << SHIFT_CMD_CODE); // reset 4 bits + pio_val |= (0xF & cmd) << SHIFT_CMD_CODE; // set command + *p_pio_ctrl = pio_val; + usleep(2); + pio_val &= ~(0xF << SHIFT_CMD_CODE); // reset 4 bits + pio_val |= CMD_IDLE << SHIFT_CMD_CODE; // set idle command + *p_pio_ctrl = pio_val; + // alt_u32 mask = (0xF & cmd) << SHIFT_CMD_CODE; + //*p_pio_ctrl &= ~mask; //set 0's + //*p_pio_ctrl |= mask; //set 1's + // usleep(2); + // SYNC_set_command(CMD_IDLE); +} + +void SYNC_reset_bcid() { SYNC_set_command(CMD_RESET_BCID); } + +void SYNC_start_acq() { SYNC_set_command(CMD_START_ACQ); } + +void SYNC_stop_acq() { SYNC_set_command(CMD_STOP_ACQ); } + +void SYNC_ramfull_ext() { SYNC_set_command(CMD_RAMFULL_EXT); } + +void SYNC_digital_ro() { SYNC_set_command(CMD_DIGITAL_RO); } + +void SYNC_fifo_arming() { *p_pio_ctrl |= O_HPS_BUSY; } + +void SYNC_fifo_ack() { + *p_pio_ctrl &= ~O_HPS_BUSY; // falling edge on hps_busy + while (SYNC_state() != S_IDLE) { + ; + } // when FGPA ready for new acqui + *p_pio_ctrl |= O_HPS_BUSY; // rearming +} + +int SYNC_dcc_cmd_mem() { return ((*p_pio_cnt24 >> SHIFT_CMD_CODE_MEM) & 0xF); } + +int SYNC_dcc_cmd_now() { return ((*p_pio_cnt24 >> SHIFT_CMD_CODE_NOW) & 0xF); } + +int SYNC_ramfull() { + int state = SYNC_state(); + if (state == S_RAMFULL) + return (1); + else + return (0); +} + +int SYNC_fpga_ro() { + int state = SYNC_state(); + if ((state == S_START_RO) || (state == S_WAIT_END_RO)) + return (1); + else + return (0); +} + +int SYNC_fifo_ready() { + int state = SYNC_state(); + if (state == S_FIFO_READY) + return (1); + else + return (0); +} + +int SYNC_run_stopped() { + int state = SYNC_state(); + if (state == S_STOP_RUN) + return (1); + else + return (0); +} + +int SYNC_hr_transmiton(int rfm) { + switch (rfm) { + case 0: + return ((*p_pio_state & O_HR_TRANSMITON_0) == O_HR_TRANSMITON_0); + case 1: + return ((*p_pio_state & O_HR_TRANSMITON_1) == O_HR_TRANSMITON_1); + case 2: + return ((*p_pio_state & O_HR_TRANSMITON_2) == O_HR_TRANSMITON_2); + case 3: + return ((*p_pio_state & O_HR_TRANSMITON_3) == O_HR_TRANSMITON_3); + default: + fprintf(stderr, "hr_transmiton: bad argument %d\n", rfm); + return (-1); + } +} + +int SYNC_chipsat(int rfm) { + switch (rfm) { + case 0: + return ((*p_pio_state & O_CHIPSAT_0) == O_CHIPSAT_0); + case 1: + return ((*p_pio_state & O_CHIPSAT_1) == O_CHIPSAT_1); + case 2: + return ((*p_pio_state & O_CHIPSAT_2) == O_CHIPSAT_2); + case 3: + return ((*p_pio_state & O_CHIPSAT_3) == O_CHIPSAT_3); + default: + fprintf(stderr, "chipsat: bad argument %d\n", rfm); + return (-1); + } +} + +int SYNC_hr_end_ro(int rfm) { + switch (rfm) { + case 0: + return ((*p_pio_state & O_HR_END_RO_0) == O_HR_END_RO_0); + case 1: + return ((*p_pio_state & O_HR_END_RO_1) == O_HR_END_RO_1); + case 2: + return ((*p_pio_state & O_HR_END_RO_2) == O_HR_END_RO_2); + case 3: + return ((*p_pio_state & O_HR_END_RO_3) == O_HR_END_RO_3); + default: + fprintf(stderr, "hr_end_ro: bad argument %d\n", rfm); + return (-1); + } +} + +void SYNC_enable_dcc_busy() { *p_pio_ctrl |= O_ENA_DCC_BUSY; } + +void SYNC_enable_dcc_ramfull() { *p_pio_ctrl |= O_ENA_DCC_RAMFULL; } + +/*************** trigger configuration *****************/ + +void TRIG_select_thresh_0() { *p_pio_ctrl &= ~O_SEL_TRIG_THRESH; }; + +void TRIG_select_thresh_1() { *p_pio_ctrl |= O_SEL_TRIG_THRESH; }; + +void TRIG_enable() { *p_pio_ctrl |= O_ENA_TRIG; }; + +void TRIG_disable() { *p_pio_ctrl &= ~O_ENA_TRIG; }; + +/*************** counters (time, events) ***************/ + +void CNT_reset() { + *p_pio_ctrl |= O_RST_SCALERS; + // fprintf(stderr,"reset scaler\n"); + usleep(1); + *p_pio_ctrl &= ~O_RST_SCALERS; +} + +void CNT_start() { *p_pio_ctrl |= O_ENA_SCALERS; } + +void CNT_stop() { *p_pio_ctrl &= ~O_ENA_SCALERS; } + +alt_u32 CNT_hit0(int rfm) { return (*p_pio_cnt_hit0[rfm]); } + +alt_u32 CNT_hit1(int rfm) { return (*p_pio_cnt_hit1[rfm]); } + +alt_u32 CNT_trig() { return (*p_pio_cnt_trig); } + +alt_u32 CNT_bcid24() { return (*p_pio_cnt24 & 0xFFFFFF); } + +alt_u32 CNT_bcid48msb() { return (*p_pio_cnt48_msb & 0xFFFF); } + +alt_u32 CNT_bcid48lsb() { return (*p_pio_cnt48_lsb); } + +// write counters in a file +// format: 7 x 32-bit words per ramfull-trigger +void CNT_save(FILE *output, int rfm) { + alt_u32 word32; + alt_u32 dif_id = EDA_DIF_ID_OFFS + ((this_eda_id & 7) << 3) + (rfm & 3); + word32 = daq_cycle_id[rfm]; + word32 = (dif_id << 24) | (word32 & 0x00FFFFFF); + fwrite_u32(word32, output); + fwrite_u32(CNT_bcid48msb(), output); + fwrite_u32(CNT_bcid48lsb(), output); + fwrite_u32(CNT_bcid24(), output); + fwrite_u32(CNT_hit0(rfm), output); + fwrite_u32(CNT_hit1(rfm), output); + fwrite_u32(CNT_trig(), output); + return; +} + +/****************** Hardroc slow control **************/ + +void HRSC_select_slow_control() { *p_pio_ctrl &= ~O_SELECT_SC_RR; } + +void HRSC_select_read_register() { *p_pio_ctrl |= O_SELECT_SC_RR; } + +void HRSC_set_bit(alt_u32 hr_addr, alt_u32 bit_addr, alt_u32 bit) { + // byte address 0 corresponds to the last register (bit_addr 864 to 871) of + // the last Hardroc (pos=NB_HR-1) + div_t frac = div(bit_addr, 8); + alt_u32 byte_addr = + (NB_HR - 1 - hr_addr) * NB_BYTES_CFG_HR + NB_BYTES_CFG_HR - 1 - frac.quot; + uint8_t byte = hr_config_data[byte_addr]; + alt_u32 bit_offset = frac.rem; + // bit address increases from lsb to msb + uint8_t mask1 = 0x01 << bit_offset; + uint8_t mask2 = (0x01 & bit) << bit_offset; + byte &= ~mask1; // reset target bit + byte |= mask2; // set target bit = "bit" argument + hr_config_data[byte_addr] = byte; + return; +} + +alt_u32 HRSC_get_bit(alt_u32 hr_addr, alt_u32 bit_addr) { + // byte address 0 corresponds to the last register (bit_addr 864-871) of the + // last Hardroc (pos=NB_HR-1) + div_t frac = div(bit_addr, 8); + alt_u32 byte_addr = + (NB_HR - 1 - hr_addr) * NB_BYTES_CFG_HR + NB_BYTES_CFG_HR - 1 - frac.quot; + uint8_t byte = hr_config_data[byte_addr]; + alt_u32 bit_offset = frac.rem; + return ((byte >> bit_offset) & 0x01); +} + +void HRSC_set_word(alt_u32 hr_addr, alt_u32 bit_addr, alt_u32 n_bits, + alt_u32 value) { + int i; + for (i = 0; i < n_bits; i++) { // scan lsb to msb + alt_u32 bit = (value >> i) & 0x01; + HRSC_set_bit(hr_addr, bit_addr + i, bit); + } + return; +} + +void HRSC_set_word_msb2lsb(alt_u32 hr_addr, alt_u32 bit_addr, alt_u32 n_bits, + alt_u32 value) { + int i; + for (i = 0; i < n_bits; i++) { // scan msb to lsb + alt_u32 bit = (value >> i) & 0x01; + HRSC_set_bit(hr_addr, bit_addr + n_bits - 1 - i, bit); + } + return; +} + +int HRSC_read_conf_singl(FILE *file_conf, alt_u32 hr_addr) { + ssize_t n = 0; + alt_u32 bit_addr, bit; + alt_u32 cnt = NB_BITS_CFG_HR - 1; + int delimiter = ';'; + char *str = NULL; + size_t len = 0; + // printf("bit_addr\tbit_value\n"); + while (!feof(file_conf)) { + // bit addr + n = getdelim(&str, &len, delimiter, file_conf); + if (n == 0) { + continue; + } + if (str[0] == '#') { // if comment line, skip + n = getline(&str, &len, file_conf); + if (n == 0) { + continue; + } + n = getdelim(&str, &len, delimiter, file_conf); + if (n == 0) { + continue; + } + } + bit_addr = atoi(str); + // skip reg name and indices + n = getdelim(&str, &len, delimiter, file_conf); + if (n == 0) { + continue; + } + n = getdelim(&str, &len, delimiter, file_conf); + if (n == 0) { + continue; + } + n = getdelim(&str, &len, delimiter, file_conf); + if (n == 0) { + continue; + } + // bit value + n = getline(&str, &len, file_conf); + if (n == 0) { + continue; + } + bit = atoi(str); + // check bit address + if (bit_addr != cnt) { + printf("config file error: bit address %u expected, %s found\n", + (uint32_t)cnt, str); + return (-1); + } + cnt--; + // set bit + // printf("%u\t%u\n",bit_addr,bit); + HRSC_set_bit(hr_addr, bit_addr, bit); + if (bit_addr == 0) + return (1); + } + printf("config file error: end of file found before last bit\n"); + return (-1); +} + +void HRSC_copy_conf(alt_u32 hr_addr_source, alt_u32 hr_addr_dest) { + alt_u32 source_addr = (NB_HR - 1 - hr_addr_source) * NB_BYTES_CFG_HR; + alt_u32 dest_addr = (NB_HR - 1 - hr_addr_dest) * NB_BYTES_CFG_HR; + memcpy(hr_config_data + dest_addr, hr_config_data + source_addr, + NB_BYTES_CFG_HR); + return; +} + +int HRSC_read_conf_mult(FILE *file_conf) { + ssize_t n = 0; + alt_u32 hr_addr, bit_addr, bit; + alt_u32 cnt_bit = NB_BITS_CFG_HR - 1; + alt_u32 cnt_hr = NB_HR - 1; + int delimiter = ';'; + char *str = NULL; + size_t len = 0; + while (!feof(file_conf) && (cnt_bit >= 0) && (cnt_hr >= 0)) { + // hr_addr + n = getdelim(&str, &len, delimiter, file_conf); + if (n == 0) { + continue; + } + hr_addr = atoi(str); + // bit addr + n = getdelim(&str, &len, delimiter, file_conf); + if (n == 0) { + continue; + } + bit_addr = atoi(str); + // bit value + n = getline(&str, &len, file_conf); + if (n == 0) { + continue; + } + bit = atoi(str); + // check hr and bit addresses + if (bit_addr != cnt_bit) { + printf("format error: bit address %u expected, %u found\n", + (uint32_t)cnt_bit, (uint32_t)bit_addr); + return (-1); + } + if (hr_addr != cnt_hr) { + printf("format error: HR address %u expected, %u found\n", + (uint32_t)cnt_hr, (uint32_t)hr_addr); + return (-1); + } + cnt_bit--; + if (cnt_bit == 0) { + cnt_bit = NB_BITS_CFG_HR - 1; + cnt_hr--; + } + // set bit + HRSC_set_bit(hr_addr, bit_addr, bit); + if ((bit_addr == 0) && (hr_addr == 0)) + return (1); + } + printf("config file error: end of file found before last bit\n"); + return (-1); +} + +int HRSC_write_conf_mult(FILE *file_conf) { + alt_u32 i, j; + alt_u32 cnt_bit, cnt_hr; + for (i = 0; i < NB_HR; i++) { + for (j = 0; j < NB_BITS_CFG_HR; j++) { + cnt_hr = NB_HR - 1 - i; + cnt_bit = NB_BITS_CFG_HR - 1 - j; + fprintf(file_conf, "%u;%u;%u\n", (uint32_t)cnt_hr, (uint32_t)cnt_bit, + (uint32_t)HRSC_get_bit(cnt_hr, cnt_bit)); + } + } + return (1); +} + +void HRSC_set_ctest(alt_u32 hr_addr, alt_u32 chan, + alt_u32 val) { // switch for test capacitor (1=closed) + HRSC_set_bit(hr_addr, chan, (val & 0x01)); +} + +void HRSC_set_all_ctest_off() { + alt_u32 hr_addr, chan; + for (hr_addr = 0; hr_addr < NB_HR; hr_addr++) + for (chan = 0; chan < 64; chan++) + HRSC_set_ctest(hr_addr, chan, 0); +} + +void HRSC_set_preamp(alt_u32 hr_addr, alt_u32 chan, alt_u32 val) { + alt_u32 bit_addr = 64 + 8 * chan; + HRSC_set_word(hr_addr, bit_addr, 8, val); +} + +void HRSC_set_cmd_fsb2(alt_u32 hr_addr, alt_u32 chan, + alt_u32 val) { // fast shaper 2 gain + alt_u32 bit_addr = 587 + 4 * chan; + HRSC_set_word_msb2lsb(hr_addr, bit_addr, 4, + (~val)); //"cmdb" register bits are active-low +} + +void HRSC_set_cmd_fsb1(alt_u32 hr_addr, alt_u32 chan, + alt_u32 val) { // fast shaper 2 gain + alt_u32 bit_addr = 595 + 4 * chan; + HRSC_set_word_msb2lsb(hr_addr, bit_addr, 4, + (~val)); //"cmdb" register bits are active-low +} + +void HRSC_set_mask(alt_u32 hr_addr, alt_u32 chan, alt_u32 val) { + alt_u32 bit_addr = 618 + 3 * chan; + HRSC_set_word(hr_addr, bit_addr, 3, val); +} + +void HRSC_set_chip_id(alt_u32 hr_addr, alt_u32 val) { + HRSC_set_word_msb2lsb(hr_addr, 810, 8, val); +} + +void HRSC_set_DAC0(alt_u32 hr_addr, alt_u32 val) { + HRSC_set_word(hr_addr, 818, 10, val); +} + +void HRSC_set_DAC1(alt_u32 hr_addr, alt_u32 val) { + HRSC_set_word(hr_addr, 828, 10, val); +} + +void HRSC_set_DAC2(alt_u32 hr_addr, alt_u32 val) { + HRSC_set_word(hr_addr, 838, 10, val); +} + +void HRSC_set_DAC_coarse(alt_u32 hr_addr) { HRSC_set_bit(hr_addr, 848, 0); } + +void HRSC_set_DAC_fine(alt_u32 hr_addr) { HRSC_set_bit(hr_addr, 848, 1); } + +void HRSC_set_shaper_capa(alt_u32 hr_addr, alt_u32 val) { + HRSC_set_bit(hr_addr, 611, (val & 1)); // sw_50f0 = b0 + HRSC_set_bit(hr_addr, 602, (val & 1)); // sw_50f1 = b0 + HRSC_set_bit(hr_addr, 594, (val & 1)); // sw_50f2 = b0 + HRSC_set_bit(hr_addr, 610, ((val >> 1) & 1)); // sw_100f0 = b1 + HRSC_set_bit(hr_addr, 601, ((val >> 1) & 1)); // sw_100f1 = b1 + HRSC_set_bit(hr_addr, 593, ((val >> 1) & 1)); // sw_100f2 = b1 +} + +void HRSC_set_shaper_resis(alt_u32 hr_addr, alt_u32 val) { + HRSC_set_bit(hr_addr, 609, (val & 1)); // sw_100k0 = b0 + HRSC_set_bit(hr_addr, 600, (val & 1)); // sw_100k1 = b0 + HRSC_set_bit(hr_addr, 592, (val & 1)); // sw_100k2 = b0 + HRSC_set_bit(hr_addr, 608, ((val >> 1) & 1)); // sw_50k0 = b1 + HRSC_set_bit(hr_addr, 599, ((val >> 1) & 1)); // sw_50k1 = b1 + HRSC_set_bit(hr_addr, 591, ((val >> 1) & 1)); // sw_50k2 = b1 +} + +int HRSC_set_config(int rfm) { + // change the control word + alt_u32 checkword; + if ((*p_pio_sc_check[rfm]) == 0xCAFEFADE) + checkword = 0x36BAFFE5; + else + checkword = 0xCAFEFADE; + hr_config_buffer[0] = (checkword >> 24) & 0xFF; + hr_config_buffer[1] = (checkword >> 16) & 0xFF; + hr_config_buffer[2] = (checkword >> 8) & 0xFF; + hr_config_buffer[3] = checkword & 0xFF; + // reset sc + HRSC_select_slow_control(); + HRSC_reset_sc(); + if (HRSC_sc_done(rfm)) { + fprintf(stderr, "error: slow control reset failed\n"); + return (-1); + } + // copy to fpga + void *dest = (void *)p_ram_sc[rfm]; + memcpy(dest, hr_config_buffer, NB_HR * NB_BYTES_CFG_HR + 4); + // trigger the slow control serializer + HRSC_start_sc(rfm); + // check loopback header + usleep(10); + while (!HRSC_sc_done(rfm)) + usleep(10); + if (*p_pio_sc_check[rfm] != checkword) { + fprintf(stderr, "loopback register=%x\n", (uint32_t)(*p_pio_sc_check[rfm])); + return (-1); + } + return (1); +} + +int HRSC_reset_read_registers(int rfm) { + // change the control word + alt_u32 checkword; + if ((*p_pio_sc_check[rfm]) == 0xCAFEFADE) + checkword = 0x36BAFFE5; + else + checkword = 0xCAFEFADE; + alt_u8 buffer[NB_HR * NB_BYTES_CFG_HR + 4] = {0}; + int offset = NB_HR * NB_BYTES_CFG_HR - 64; + buffer[offset] = (checkword >> 24) & 0xFF; + buffer[offset + 1] = (checkword >> 16) & 0xFF; + buffer[offset + 2] = (checkword >> 8) & 0xFF; + buffer[offset + 3] = checkword & 0xFF; + // reset sc + HRSC_select_read_register(); + HRSC_reset_sc(); + if (HRSC_sc_done(rfm)) { + fprintf(stderr, "error: slow control reset failed\n"); + return (-1); + } + // copy to fpga + void *dest = (void *)p_ram_sc[rfm]; + memcpy(dest, buffer, NB_HR * NB_BYTES_CFG_HR + 4); + // trigger the slow control serializer + usleep(10); + HRSC_start_sc(rfm); + // check loopback header + usleep(10); + while (!HRSC_sc_done(rfm)) + usleep(10); + if (*p_pio_sc_check[rfm] != checkword) { + fprintf(stderr, "loopback register=%x\n", (uint32_t)(*p_pio_sc_check[rfm])); + return (-1); + } + HRSC_select_slow_control(); + return (1); +} + +int HRSC_set_read_register(int rfm, int channel) { + // change the control word + alt_u32 checkword; + if ((*p_pio_sc_check[rfm]) == 0xCAFEFADE) + checkword = 0x36BAFFE5; + else + checkword = 0xCAFEFADE; + alt_u8 buffer[NB_HR * NB_BYTES_CFG_HR + 4] = {0}; + int offset = NB_HR * NB_BYTES_CFG_HR - 64; + buffer[offset] = (checkword >> 24) & 0xFF; + buffer[offset + 1] = (checkword >> 16) & 0xFF; + buffer[offset + 2] = (checkword >> 8) & 0xFF; + buffer[offset + 3] = checkword & 0xFF; + // reset sc + HRSC_select_read_register(); + HRSC_reset_sc(); + if (HRSC_sc_done(rfm)) { + fprintf(stderr, "error: slow control reset failed\n"); + return (-1); + } + // select the same channel for all HR's + div_t frac = div(channel, 8); // channel: 0 to 64*NB_HR-1 + alt_u8 byte = 0x1 << frac.rem; + int i; + for (i = 0; i < 8; i++) { + offset = NB_HR * NB_BYTES_CFG_HR + 3 - i * 8 - + frac.quot; // last byte -> channels 0 to 7 + buffer[offset] = byte; + } + // copy to fpga + void *dest = (void *)p_ram_sc[rfm]; + memcpy(dest, buffer, NB_HR * NB_BYTES_CFG_HR + 4); + // trigger the slow control serializer + HRSC_start_sc(rfm); + // check loopback header + usleep(10); + while (!HRSC_sc_done(rfm)) + usleep(10); + if (*p_pio_sc_check[rfm] != checkword) { + fprintf(stderr, "loopback register=%x\n", (uint32_t)(*p_pio_sc_check[rfm])); + return (-1); + } + HRSC_select_slow_control(); + return (1); +} + +void HRSC_reset_sc() { + *p_pio_ctrl |= O_RESET_SC; + // fprintf(stderr,"Reset slow control\n"); + usleep(1); + *p_pio_ctrl &= ~O_RESET_SC; + usleep(1); + // fprintf(stderr,"reset_sc; sc_done=%d\n",sc_done(0x01)); +} + +void HRSC_start_sc(int rfm) { + switch (rfm) { + case 0: + *p_pio_ctrl |= O_START_SC_0; + usleep(1); + *p_pio_ctrl &= ~O_START_SC_0; + return; + case 1: + *p_pio_ctrl |= O_START_SC_1; + usleep(1); + *p_pio_ctrl &= ~O_START_SC_1; + return; + case 2: + *p_pio_ctrl |= O_START_SC_2; + usleep(1); + *p_pio_ctrl &= ~O_START_SC_2; + return; + case 3: + *p_pio_ctrl |= O_START_SC_3; + usleep(1); + *p_pio_ctrl &= ~O_START_SC_3; + return; + default: + fprintf(stderr, "start slow control: bad argument %d\n", rfm); + return; + } +} + +int HRSC_sc_done(int rfm) { + switch (rfm) { + case 0: + return ((*p_pio_state & O_SC_DONE_0) == O_SC_DONE_0); + case 1: + return ((*p_pio_state & O_SC_DONE_1) == O_SC_DONE_1); + case 2: + return ((*p_pio_state & O_SC_DONE_2) == O_SC_DONE_2); + case 3: + return ((*p_pio_state & O_SC_DONE_3) == O_SC_DONE_3); + default: + fprintf(stderr, "sc_done: bad argument %d\n", rfm); + return (-1); + } +} + +/********************* Hardroc DAQ *********************/ + +alt_u32 bit(alt_u32 word, alt_u32 digit) { + return ((word >> digit) & MASK_1_BIT); +} + +void DAQ_fifo_init(int rfm) { + alt_u32 *fifo_csr = p_fifo_daq_csr[rfm]; + fifo_csr[ALTERA_AVALON_FIFO_EVENT_REG] = + ALTERA_AVALON_FIFO_EVENT_ALL; // clear event reg (write 1 to each field) + fifo_csr[ALTERA_AVALON_FIFO_IENABLE_REG] = 0; // disable interrupts + fifo_csr[ALTERA_AVALON_FIFO_ALMOSTFULL_REG] = + (alt_u32)5081; // set "almostfull" to (max_size + 1) + fifo_csr[ALTERA_AVALON_FIFO_ALMOSTEMPTY_REG] = (alt_u32)2; // set + // "almostempty" + return; +} + +void DAQ_fifo_print_status(int rfm) { // print status register + alt_u32 *fifo_csr = p_fifo_daq_csr[rfm]; + fprintf(stderr, "fifo/status_reg=%04x\n", + (uint32_t)fifo_csr[ALTERA_AVALON_FIFO_STATUS_REG]); + return; +} + +void DAQ_fifo_print_event(int rfm) { // print event register + alt_u32 *fifo_csr = p_fifo_daq_csr[rfm]; + fprintf(stderr, "fifo/event_reg=%04x\n", + (uint32_t)fifo_csr[ALTERA_AVALON_FIFO_EVENT_REG]); + return; +} + +void DAQ_fifo_clear_event(int rfm) { // clear event register + alt_u32 *fifo_csr = p_fifo_daq_csr[rfm]; + fifo_csr[ALTERA_AVALON_FIFO_EVENT_REG] = + ALTERA_AVALON_FIFO_EVENT_ALL; // clear event reg (write 1 to each field) + return; +} + +int DAQ_fifo_in_valid(int rfm) { + switch (rfm) { + case 0: + return ((*p_pio_state & O_FIFO_IN_VALID_0) == O_FIFO_IN_VALID_0); + case 1: + return ((*p_pio_state & O_FIFO_IN_VALID_1) == O_FIFO_IN_VALID_1); + case 2: + return ((*p_pio_state & O_FIFO_IN_VALID_2) == O_FIFO_IN_VALID_2); + case 3: + return ((*p_pio_state & O_FIFO_IN_VALID_3) == O_FIFO_IN_VALID_3); + default: + fprintf(stderr, "fifo_in_valid: bad argument %d\n", rfm); + return (-1); + } +} + +int DAQ_fifo_full(int rfm) { + alt_u32 istatus = p_fifo_daq_csr[rfm][ALTERA_AVALON_FIFO_STATUS_REG]; + return (bit(istatus, 0)); +} + +int DAQ_fifo_empty(int rfm) { + alt_u32 istatus = p_fifo_daq_csr[rfm][ALTERA_AVALON_FIFO_STATUS_REG]; + return (bit(istatus, 1)); +} + +alt_u32 DAQ_fifo_fill_level(int rfm) { + return p_fifo_daq_csr[rfm][ALTERA_AVALON_FIFO_LEVEL_REG]; +} + +alt_u32 DAQ_fifo_data(int rfm) { + return (p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG]); +} + +alt_u32 garbage; +alt_u32 DAQ_fifo_clear(int rfm) { + alt_u32 count_words = 0; + while (!DAQ_fifo_empty(rfm)) { + garbage = p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG]; + count_words++; + } + return (count_words / 5); +} + +// a simple binary format for debug mode: +// 6 x 32-bit words per event +// 1: dif_id;dif_trigger_counter +// 2: hr_id;bcid +// 3-6 : discri data + +// note on DAQ architecture: each RFM & chamber is identified by a DIF id +// and EDA produces a data stream for each RFM. +// DIF id's for new electronics are under 100 (above 100 is for old DIFs) + +alt_u32 +DAQ_save_hr_data(FILE *output, + int rfm) { // fill the buffer and return the nb of events read + int cnt = 0; + int i; + alt_u32 word32, dif_id; + // DIF id + dif_id = EDA_DIF_ID_OFFS + ((this_eda_id & 7) << 3) + (rfm & 3); + while (!DAQ_fifo_empty(rfm)) { + word32 = (dif_id << 24) | (daq_cycle_id[rfm] & 0x00FFFFFF); + fwrite_u32(word32, output); + for (i = 0; i < 5; i++) { + fwrite_u32((p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG]), output); + cnt++; + } + } + daq_cycle_id[rfm]++; + return (cnt / 5); +} + +alt_u32 DAQ_hexdump_hr_data( + FILE *output, int rfm) { // fill the buffer and return the nb of events read + int cnt = 0; + int i; + alt_u32 word32, dif_id; + // DIF id + dif_id = EDA_DIF_ID_OFFS + ((this_eda_id & 7) << 3) + (rfm & 3); + while (!DAQ_fifo_empty(rfm)) { + word32 = (dif_id << 24) | (daq_cycle_id[rfm] & 0x00FFFFFF); + fprintf(output, "%08x\t", (uint32_t)word32); + for (i = 0; i < 5; i++) { + fprintf(output, "%08x\t", + (uint32_t)(p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG])); + cnt++; + } + fprintf(output, "\n"); + } + daq_cycle_id[rfm]++; + return (cnt / 5); +} + +alt_u32 DAQ_save_hr_data_DIF( + FILE *output, int rfm) { // fill the buffer and return the nb of events read + int cnt = 0; + int i, nb_ram_units; + alt_u8 word8; + alt_u16 word16; + alt_u32 word32; + alt_u64 bcid48; + alt_u32 bcid24; + + // DIF DAQ header + fwrite_u8((alt_u8)0xB0, output); + // DIF id + word8 = EDA_DIF_ID_OFFS + ((this_eda_id & 7) << 3) + (rfm & 3); + fwrite_u8(word8, output); + // counters + fwrite_u32(daq_cycle_id[rfm], output); + fwrite_u32(CNT_hit0(rfm), output); + fwrite_u32(CNT_hit1(rfm), output); + word16 = CNT_bcid48msb(rfm) & 0xFFFF; + bcid48 = word16; + fwrite_u16(word16, output); + word32 = CNT_bcid48lsb(rfm); + fwrite_u32(word32, output); + bcid48 = (bcid48 << 32) | word32; + bcid24 = CNT_bcid24(rfm); + word8 = bcid24 >> 16; + word16 = bcid24 & 0xFFFF; + fwrite_u8(word8, output); + fwrite_u16(word16, output); + // unused "nb_lines" + fwrite_u8(0xFF, output); + // HR DAQ chunk + int last_hr_id = -1; + int hr_id; + while (!DAQ_fifo_empty(rfm)) { + // read HR id + word32 = p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG]; + hr_id = (int)(word32 >> 24); + // if new HR id, insert (trailer and) header + if (hr_id != last_hr_id) { + if (last_hr_id >= 0) + fwrite_u8((alt_u8)0xA3, output); // hr trailer + fwrite_u8((alt_u8)0xB4, output); // hr header + } + fwrite_u32(word32, output); + for (i = 0; i < 4; i++) { + fwrite_u32((p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG]), output); + cnt++; + } + last_hr_id = hr_id; + } + fwrite_u8((alt_u8)0xA3, output); // last hr trailer + fwrite_u8((alt_u8)0xA0, output); // DIF DAQ trailer + fwrite_u16((alt_u16)0xC0C0, output); // CRC + // on-line monit + nb_ram_units = cnt / 5; + // fprintf(stderr,"%lu\t%llu\t%lu\t%d\t%.3e\n",daq_cycle_id[rfm],bcid48,bcid24,nb_ram_units,ram_rate); + + daq_cycle_id[rfm]++; + return (nb_ram_units); +} + +alt_u8 *buf_wr_8(alt_u8 *buf, alt_u8 val) { + *buf = val; + return (buf + 1); +} + +alt_u8 *buf_wr_16(alt_u8 *buf, alt_u16 val) { + alt_u16 bigend = htons(val); + memcpy(buf, &bigend, 2); + return (buf + 2); +} + +alt_u8 *buf_wr_32(alt_u8 *buf, alt_u32 val) { + alt_u32 bigend = htonl(val); + memcpy(buf, &bigend, 4); + return (buf + 4); +} + +void DAQ_bufferize_data_DIF(int rfm) { // fill a buffer with RFM data + int i; + static alt_u32 bcid48_offset; + alt_u8 word8; + alt_u16 word16; + alt_u32 word32; + alt_u64 bcid48; + alt_u32 bcid24; + + // offset + if (daq_cycle_id[rfm] == 0) + bcid48_offset = CNT_bcid48lsb(rfm) - CNT_bcid24(rfm); + // NB: at this time, CNT_bcid48msb(rfm)==0 + // DIF DAQ header + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xB0); + // DIF id + word8 = EDA_DIF_ID_OFFS + ((this_eda_id & 7) << 3) + (rfm & 3); + hr_data_cursor = buf_wr_8(hr_data_cursor, word8); + // counters + hr_data_cursor = buf_wr_32(hr_data_cursor, daq_cycle_id[rfm]); + hr_data_cursor = buf_wr_32(hr_data_cursor, CNT_hit0(rfm)); + hr_data_cursor = buf_wr_32(hr_data_cursor, CNT_hit1(rfm)); + // assemble and correct absolute BCID + bcid48 = CNT_bcid48msb(rfm); + bcid48 <<= 32; + bcid48 |= CNT_bcid48lsb(rfm); + bcid48 -= bcid48_offset; + // copy frame + word16 = (bcid48 >> 32) & 0xFFFF; + word32 = (alt_u32)bcid48; // truncates + hr_data_cursor = buf_wr_16(hr_data_cursor, word16); + hr_data_cursor = buf_wr_32(hr_data_cursor, word32); + bcid24 = CNT_bcid24(rfm); + word8 = bcid24 >> 16; + word16 = bcid24 & 0xFFFF; + hr_data_cursor = buf_wr_8(hr_data_cursor, word8); + hr_data_cursor = buf_wr_16(hr_data_cursor, word16); + // unused "nb_lines" + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xFF); + // HR DAQ chunk + int last_hr_id = -1; + int hr_id; + while (!DAQ_fifo_empty(rfm)) { + // read HR id + word32 = p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG]; + hr_id = (int)(word32 >> 24); + // if new HR id, insert (trailer and) header + if (hr_id != last_hr_id) { + if (last_hr_id >= 0) { // hr trailer + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xA3); + } + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xB4); // hr header + } + hr_data_cursor = buf_wr_32(hr_data_cursor, word32); + for (i = 0; i < 4; i++) { + hr_data_cursor = buf_wr_32(hr_data_cursor, + p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG]); + } + last_hr_id = hr_id; + } + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xA3); // last hr trailer + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xA0); // DIF DAQ trailer + hr_data_cursor = buf_wr_16(hr_data_cursor, (alt_u16)0xC0C0); // CRC + + daq_cycle_id[rfm]++; + + return; +} + +int DAQ_send_sock_buffer(int sock) { + alt_u32 size = (alt_u32)(hr_data_cursor - hr_data_buffer); + char sock_read_buf[4] = {0}; + int valread; + // wait request + valread = read(sock, sock_read_buf, 3); + if ((valread != 3) || (strcmp(sock_read_buf, "PUL") != 0)) { + fprintf(stderr, "instead of PUL, received :%s\n", sock_read_buf); + return 0; + } + // send buffer size (32 bits little endian) + int byte_weight; + for (byte_weight = 0; byte_weight < 4; byte_weight++) { + send(sock, &size, 1, 0); + size >>= 8; + } + // send buffer content + send(sock, hr_data_buffer, size, 0); + // wait ACK + valread = read(sock, sock_read_buf, 3); + if ((valread != 3) || (strcmp(sock_read_buf, "ACK") != 0)) { + fprintf(stderr, "instead of ACK, received :%s\n", sock_read_buf); + return 0; + } + // rewind write pointer + hr_data_cursor = hr_data_buffer; + return 1; +} + +int DAQ_write_buffer(FILE *f) { + ssize_t size = (hr_data_cursor - hr_data_buffer); + fwrite(hr_data_buffer, 1, size, f); + // rewind write pointer + hr_data_cursor = hr_data_buffer; + return 1; +} + +void DAQ_reset_buffer() { + // rewind write pointer + hr_data_cursor = hr_data_buffer; + return; +} + +/********************* Test injector *********************/ + +int INJ_dac_select(int rfm) { + int addr = 0b00100000 + (rfm & 0x3); + if (ioctl(i2c_file, I2C_SLAVE, addr) < 0) { + fprintf(stderr, + "Failed to acquire DAC bus access and/or talk to slave (with " + "address %d).\n", + addr); + return (-1); + } + return (1); +} + +int INJ_dac_set(alt_u32 value) { + alt_u8 frame[3]; + // command byte + frame[0] = 0; + // lsb + frame[1] = (alt_u8)(value % 0x100); + // msb + frame[2] = (alt_u8)(value >> 8); + if (write(i2c_file, frame, 3) != 3) { + fprintf(stderr, "Failed to write 3 bytes to LTC1668 DAC.\n"); + return (-1); + } + usleep(200); // for stabilisation + return (1); +} + +void INJ_start_ctest_pulser(alt_u32 half_period, alt_u32 phase) { + // half period unit = 200 ns + // phase unit = 6.25 ns + alt_u32 word32 = ((phase & 0x1F) << 16) | (half_period & 0xFFFF); + *p_pio_pulser = word32; // set timing registers + usleep(1); + *p_pio_ctrl |= O_CTEST; // enable +} + +void INJ_stop_ctest_pulser() { *p_pio_ctrl &= ~O_CTEST; } + +/********************** Printing **********************/ + +void PRINT_fifo_status(int rfm) { + alt_u32 *fifo_csr = p_fifo_daq_csr[rfm]; + fprintf(stderr, "----fifo status----------\n"); + fprintf(stderr, "fill level:\t\t%lu\n", + fifo_csr[ALTERA_AVALON_FIFO_LEVEL_REG]); + alt_u32 istatus = fifo_csr[ALTERA_AVALON_FIFO_STATUS_REG]; + fprintf(stderr, "istatus:"); + fprintf(stderr, "\t full:\t %lu", bit(istatus, 0)); + fprintf(stderr, "\t empty:\t %lu", bit(istatus, 1)); + fprintf(stderr, "\t almost full:\t %lu", bit(istatus, 2)); + fprintf(stderr, "\t almost empty:\t %lu", bit(istatus, 3)); + fprintf(stderr, "\t overflow:\t %lu", bit(istatus, 4)); + fprintf(stderr, "\t underflow:\t %lu\n", bit(istatus, 5)); + alt_u32 event = fifo_csr[ALTERA_AVALON_FIFO_EVENT_REG]; + fprintf(stderr, "event: "); + fprintf(stderr, "\t full:\t %lu", bit(event, 0)); + fprintf(stderr, "\t empty:\t %lu", bit(event, 1)); + fprintf(stderr, "\t almost full:\t %lu", bit(event, 2)); + fprintf(stderr, "\t almost empty:\t %lu", bit(event, 3)); + fprintf(stderr, "\t overflow:\t %lu", bit(event, 4)); + fprintf(stderr, "\t underflow:\t %lu\n", bit(event, 5)); + alt_u32 ienable = fifo_csr[ALTERA_AVALON_FIFO_IENABLE_REG]; + fprintf(stderr, "ienable:"); + fprintf(stderr, "\t full:\t %lu", bit(ienable, 0)); + fprintf(stderr, "\t empty:\t %lu", bit(ienable, 1)); + fprintf(stderr, "\t almost full:\t %lu", bit(ienable, 2)); + fprintf(stderr, "\t almost empty:\t %lu", bit(ienable, 3)); + fprintf(stderr, "\t overflow:\t %lu", bit(ienable, 4)); + fprintf(stderr, "\t underflow:\t %lu\n", bit(ienable, 5)); + fprintf(stderr, "almostfull:\t\t%lu\n", + fifo_csr[ALTERA_AVALON_FIFO_ALMOSTFULL_REG]); + fprintf(stderr, "almostempty:\t\t%lu\n", + fifo_csr[ALTERA_AVALON_FIFO_ALMOSTEMPTY_REG]); + fprintf(stderr, "\n\n"); + return; +} + +void PRINT_counters(FILE *output, int rfm) { + fprintf(output, "\n"); + fprintf(output, "#cycle_id;cnt_hit0;cnt_hit1;trig;"); + fprintf(output, "cnt48_msb;cnt48_lsb;cnt24\n"); + fprintf(output, "%u;%u;%u;%u;", (uint32_t)daq_cycle_id[rfm], + (uint32_t)CNT_hit0(rfm), (uint32_t)CNT_hit1(rfm), + (uint32_t)CNT_trig()); + fprintf(output, "%u;%u;%u\n", (uint32_t)CNT_bcid48msb(), + (uint32_t)CNT_bcid48lsb(), (uint32_t)CNT_bcid24()); + return; +} + +void PRINT_config(FILE *file_conf, int rfm) { + int i, j; + alt_u8 *p_ram = (alt_u8 *)&(p_ram_sc[rfm][0]); + for (i = 0; i < NB_HR * NB_BYTES_CFG_HR + 4; i++) { + j = 8 * (NB_HR * NB_BYTES_CFG_HR - i - 1); + fprintf(file_conf, "%d\t%x\n", j, p_ram[i]); + } + return; +} + +void PRINT_dump_registers(FILE *output) { + fprintf(output, "p_pio_state= %08lx\n", *p_pio_state); + fprintf(output, "p_pio_ctrl= %08lx\n", *p_pio_ctrl); + fprintf(output, "p_pio_pulser= %08lx\n", *p_pio_pulser); + + fprintf(output, "p_pio_cnt_hit0[0]= %08lx\n", *p_pio_cnt_hit0[0]); + fprintf(output, "p_pio_cnt_hit0[1]= %08lx\n", *p_pio_cnt_hit0[1]); + fprintf(output, "p_pio_cnt_hit0[2]= %08lx\n", *p_pio_cnt_hit0[2]); + fprintf(output, "p_pio_cnt_hit0[3]= %08lx\n", *p_pio_cnt_hit0[3]); + + fprintf(output, "p_pio_cnt_hit1[0]= %08lx\n", *p_pio_cnt_hit1[0]); + fprintf(output, "p_pio_cnt_hit1[1]= %08lx\n", *p_pio_cnt_hit1[1]); + fprintf(output, "p_pio_cnt_hit1[2]= %08lx\n", *p_pio_cnt_hit1[2]); + fprintf(output, "p_pio_cnt_hit1[3]= %08lx\n", *p_pio_cnt_hit1[3]); + + fprintf(output, "p_pio_cnt_trig= %08lx\n", *p_pio_cnt_trig); + fprintf(output, "p_pio_cnt48_msb= %08lx\n", *p_pio_cnt48_msb); + fprintf(output, "p_pio_cnt48_lsb= %08lx\n", *p_pio_cnt48_lsb); + + fprintf(output, "p_fifo_daq_csr[0]= %08lx\n", *p_fifo_daq_csr[0]); + fprintf(output, "p_fifo_daq_csr[1]= %08lx\n", *p_fifo_daq_csr[1]); + fprintf(output, "p_fifo_daq_csr[2]= %08lx\n", *p_fifo_daq_csr[2]); + fprintf(output, "p_fifo_daq_csr[3]= %08lx\n", *p_fifo_daq_csr[3]); + + char *statename[9]; + statename[0] = "idle"; + statename[1] = "reset_cnt48"; + statename[2] = "reset_cnt24"; + statename[3] = "acquiring"; + statename[4] = "ramfull"; + statename[5] = "start readout"; + statename[6] = "wait end_readout"; + statename[7] = "fifo ready"; + statename[8] = "stop run"; + int state = SYNC_state(); + fprintf(output, "synchro FSM state = %d:%s\n", state, statename[state]); + + return; +} + +void device_daq_write_dif(Device_t *ctx, int slot) { + int i; + int rfm = slot; + alt_u8 word8; + alt_u16 word16; + alt_u32 word32; + alt_u64 bcid48; + alt_u32 bcid24; + + ctx->task[rfm].beg = hr_data_cursor; + // offset + if (daq_cycle_id[rfm] == 0) { + ctx->task[rfm].bcid48_offset = CNT_bcid48lsb(rfm) - CNT_bcid24(rfm); + } + + // NB: at this time, CNT_bcid48msb(rfm)==0 + // DIF DAQ header + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xB0); + // DIF id + word8 = ctx->task[slot] + .dif; // EDA_DIF_ID_OFFS + ((this_eda_id & 7) << 3) + (rfm & 3); + hr_data_cursor = buf_wr_8(hr_data_cursor, word8); + // counters + hr_data_cursor = buf_wr_32(hr_data_cursor, daq_cycle_id[rfm]); + hr_data_cursor = buf_wr_32(hr_data_cursor, CNT_hit0(rfm)); + hr_data_cursor = buf_wr_32(hr_data_cursor, CNT_hit1(rfm)); + // assemble and correct absolute BCID + bcid48 = CNT_bcid48msb(rfm); + bcid48 <<= 32; + bcid48 |= CNT_bcid48lsb(rfm); + bcid48 -= ctx->task[rfm].bcid48_offset; + // copy frame + word16 = (bcid48 >> 32) & 0xFFFF; + word32 = (alt_u32)bcid48; // truncates + hr_data_cursor = buf_wr_16(hr_data_cursor, word16); + hr_data_cursor = buf_wr_32(hr_data_cursor, word32); + bcid24 = CNT_bcid24(rfm); + word8 = bcid24 >> 16; + word16 = bcid24 & 0xFFFF; + hr_data_cursor = buf_wr_8(hr_data_cursor, word8); + hr_data_cursor = buf_wr_16(hr_data_cursor, word16); + // unused "nb_lines" + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xFF); + // HR DAQ chunk + int last_hr_id = -1; + int hr_id; + while (!DAQ_fifo_empty(rfm)) { + // read HR id + word32 = p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG]; + hr_id = (int)(word32 >> 24); + // if new HR id, insert (trailer and) header + if (hr_id != last_hr_id) { + if (last_hr_id >= 0) { // hr trailer + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xA3); + } + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xB4); // hr header + } + hr_data_cursor = buf_wr_32(hr_data_cursor, word32); + for (i = 0; i < 4; i++) { + hr_data_cursor = buf_wr_32(hr_data_cursor, + p_fifo_daq[rfm][ALTERA_AVALON_FIFO_DATA_REG]); + } + last_hr_id = hr_id; + } + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xA3); // last hr trailer + hr_data_cursor = buf_wr_8(hr_data_cursor, (alt_u8)0xA0); // DIF DAQ trailer + hr_data_cursor = buf_wr_16(hr_data_cursor, (alt_u16)0xC0C0); // CRC + + ctx->task[rfm].end = hr_data_cursor; + daq_cycle_id[rfm]++; + return; +} diff --git a/eda/fpga.h b/eda/fpga.h new file mode 100644 index 0000000..b52f74e --- /dev/null +++ b/eda/fpga.h @@ -0,0 +1,349 @@ +#ifndef EDA_FPGA_H +#define EDA_FPGA_H 1 + +/* + * Copyright 2020 Baptiste Joly + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#include + +#ifdef TMV_ENV +#define soc_cv_av // choose cyclone V SoC +#include "hwlib.h" +#include "soc_cv_av/socal/alt_gpio.h" +#include "soc_cv_av/socal/hps.h" +#include "soc_cv_av/socal/socal.h" +#endif + +//#include "altera_avalon_pio_regs.h" +#include "altera_avalon_fifo_regs.h" + +#ifndef TMV_ENV +#define ALT_LWFPGASLVS_OFST 0xff200000 +#else +/* defined in "soc_cv_av/socal/hps.h" + + lightweight hps2fpga bridge base address: + +#define ALT_LWFPGASLVS_OFST 0xff200000 + + */ +#endif + +#define NB_HR 8 +#define NB_RFM 4 +#define NB_BITS_CFG_HR 872 +#define NB_BYTES_CFG_HR 109 + +// settings for the lightweight HPS to FPGA bridge +#define LW_H2F_BASE (ALT_LWFPGASLVS_OFST) +#define LW_H2F_SPAN (0x00100000) // enough +#define LW_H2F_MASK (LW_H2F_SPAN - 1) + +// setting for the HPS to FPGA AXI Bridge +#define H2F_BASE (0xC0000000) +#define H2F_SPAN (0x00100000) // enough +#define H2F_MASK (H2F_SPAN - 1) + +// slave addresses (prefixed by the bridge used) +// see ../FPGA_SoC/soc_system/soc_system.html +#define H2F_FIFO_DAQ_RFM0 0x00000098 +#define H2F_FIFO_DAQ_RFM1 0x00000090 +#define H2F_FIFO_DAQ_RFM2 0x00000088 +#define H2F_FIFO_DAQ_RFM3 0x00000080 + +#define H2F_FIFO_DAQ_CSR_RFM0 0x00000040 +#define H2F_FIFO_DAQ_CSR_RFM1 0x00000060 +#define H2F_FIFO_DAQ_CSR_RFM2 0x00000020 +#define H2F_FIFO_DAQ_CSR_RFM3 0x00000000 + +#define LW_H2F_LED_PIO 0x00010040 +#define LW_H2F_BUTTON_PIO 0x000100C0 + +#define LW_H2F_RAM_SC_RFM0 0x00011000 +#define LW_H2F_RAM_SC_RFM1 0x00010C00 +#define LW_H2F_RAM_SC_RFM2 0x00010800 +#define LW_H2F_RAM_SC_RFM3 0x00010400 + +#define LW_H2F_PIO_SC_CHECK_RFM0 0x00010290 +#define LW_H2F_PIO_SC_CHECK_RFM1 0x00010210 +#define LW_H2F_PIO_SC_CHECK_RFM2 0x000100F0 +#define LW_H2F_PIO_SC_CHECK_RFM3 0x00010200 + +#define LW_H2F_PIO_STATE_IN 0x000100A0 +#define LW_H2F_PIO_INTERRUPTS_BUS 0x00010300 +#define LW_H2F_PIO_INTERRUPTS_MASK 0x00010302 +#define LW_H2F_PIO_CTRL_OUT 0x00010060 +#define LW_H2F_PIO_PULSER 0x00010020 + +#define LW_H2F_PIO_CNT_HIT0_RFM0 0x00010280 +#define LW_H2F_PIO_CNT_HIT0_RFM1 0x00010260 +#define LW_H2F_PIO_CNT_HIT0_RFM2 0x000100D0 +#define LW_H2F_PIO_CNT_HIT0_RFM3 0x00010050 + +#define LW_H2F_PIO_CNT_HIT1_RFM0 0x00010270 +#define LW_H2F_PIO_CNT_HIT1_RFM1 0x000100E0 +#define LW_H2F_PIO_CNT_HIT1_RFM2 0x00010090 +#define LW_H2F_PIO_CNT_HIT1_RFM3 0x00010010 + +#define LW_H2F_PIO_CNT_TRIG 0x00010250 +#define LW_H2F_PIO_CNT48_MSB 0x00010240 +#define LW_H2F_PIO_CNT48_LSB 0x00010230 +#define LW_H2F_PIO_CNT24 0x00010220 + +// masks for PIO_STATE_IN +#define O_HR_TRANSMITON_0 0x00000001 +#define O_CHIPSAT_0 0x00000002 +#define O_HR_END_RO_0 0x00000004 +#define O_SC_DONE_0 0x00000008 +#define O_FIFO_IN_VALID_0 0x00000010 +#define O_ALERT_0 0x00000020 + +#define O_HR_TRANSMITON_1 0x00000040 +#define O_CHIPSAT_1 0x00000080 +#define O_HR_END_RO_1 0x00000100 +#define O_SC_DONE_1 0x00000200 +#define O_FIFO_IN_VALID_1 0x00000400 +#define O_ALERT_1 0x00000800 + +#define O_HR_TRANSMITON_2 0x00001000 +#define O_CHIPSAT_2 0x00002000 +#define O_HR_END_RO_2 0x00004000 +#define O_SC_DONE_2 0x00008000 +#define O_FIFO_IN_VALID_2 0x00010000 +#define O_ALERT_2 0x00020000 + +#define O_HR_TRANSMITON_3 0x00040000 +#define O_CHIPSAT_3 0x00080000 +#define O_HR_END_RO_3 0x00100000 +#define O_SC_DONE_3 0x00200000 +#define O_FIFO_IN_VALID_3 0x00400000 +#define O_ALERT_3 0x00800000 + +#define O_PLL_LCK 0x01000000 + +#define SHIFT_SYNCHRO_STATE 28 + +// mask for sDCC command monitoring (via PIO_CNT24) +#define SHIFT_CMD_CODE_NOW 24 +#define SHIFT_CMD_CODE_MEM 28 + +// masks for PIO_CTRL_OUT +#define O_RESET 0x00000001 +#define O_ON_OFF_RFM0 0x00000002 +#define O_ON_OFF_RFM1 0x00000004 +#define O_ON_OFF_RFM2 0x00000008 +#define O_ON_OFF_RFM3 0x00000010 +#define O_ENA_RFM0 0x00000020 +#define O_ENA_RFM1 0x00000040 +#define O_ENA_RFM2 0x00000080 +#define O_ENA_RFM3 0x00000100 +#define O_RESET_HR 0x00000200 +#define O_SELECT_SC_RR 0x00000400 +#define O_RESET_SC 0x00000800 +#define O_START_SC_0 0x00001000 +#define O_START_SC_1 0x00002000 +#define O_START_SC_2 0x00004000 +#define O_START_SC_3 0x00008000 +#define O_SEL_CMD_SOURCE 0x00010000 +#define SHIFT_CMD_CODE 17 +#define O_RST_SCALERS 0x00200000 +#define O_ENA_SCALERS 0x00400000 +#define O_SEL_TRIG_THRESH 0x00800000 +#define O_ENA_TRIG 0x01000000 +#define O_CTEST 0x02000000 +#define O_HPS_BUSY 0x04000000 +#define O_ENA_DCC_BUSY 0x08000000 +#define O_ENA_DCC_RAMFULL 0x10000000 +#define O_BBL_RST 0x20000000 + +#define EDA_DIF_ID_OFFS 0x00 + +// synchro states +#define S_IDLE 0 +#define S_RST_CNT48 1 +#define S_RST_CNT24 2 +#define S_ACQ 3 +#define S_RAMFULL 4 +#define S_START_RO 5 +#define S_WAIT_END_RO 6 +#define S_FIFO_READY 7 +#define S_STOP_RUN 8 + +// interrupts +#define PIO_IRQ 3 +#define IRQ_RST_BCID 1 +#define IRQ_ACQ 2 +#define IRQ_RO 4 +#define IRQ_FIFO_READY 8 + +// commands +#define CMD_RESET_BCID 1 +#define CMD_START_ACQ 2 +#define CMD_RAMFULL_EXT 3 +#define CMD_STOP_ACQ 5 +#define CMD_DIGITAL_RO 6 +#define CMD_IDLE 0xE + +// memory-mapping +int mmap_lw_h2f(int fd); +int mmap_h2f(int fd); +int munmap_lw_h2f(int fd); +int munmap_h2f(int fd); + +// pio general +void PIO_set_defaults(); +alt_u32 PIO_state_get(); +alt_u32 PIO_ctrl_get(); + +// i2c general +int I2C_init(); +void I2C_close(); + +// RFM on-off, enable +void RFM_on(int rfm); +void RFM_off(int rfm); +void RFM_enable(int rfm); +void RFM_disable(int rfm); + +// monitoring +int MONIT_pwr_alert(int rfm); +int MONIT_PTH_select(); +int MONIT_PTH_read_id(); +int MONIT_PT100_select(); +int MONIT_PT100_write_reg(alt_u8 reg_addr, alt_u8 value); +int MONIT_PWRMON_select(int rfm); +int MONIT_PWRMON_readreg(alt_u8 reg_addr); +void MONIT_BBL_reset(); + +// clocks and synchronization +void SYNC_reset_fpga(); +void SYNC_reset_hr(); +int SYNC_pll_lck(); +int SYNC_state(); +void SYNC_select_command_soft(); +void SYNC_select_command_dcc(); +void SYNC_set_command(alt_u32 cmd); +void SYNC_reset_bcid(); +void SYNC_start_acq(); +void SYNC_stop_acq(); +void SYNC_ramfull_ext(); +void SYNC_digital_ro(); +void SYNC_fifo_arming(); +void SYNC_fifo_ack(); +int SYNC_dcc_cmd_mem(); +int SYNC_dcc_cmd_now(); +int SYNC_ramfull(); +int SYNC_fpga_ro(); +int SYNC_fifo_ready(); +int SYNC_run_stopped(); +int SYNC_hr_transmiton(int rfm); +int SYNC_chipsat(int rfm); +int SYNC_hr_end_ro(int rfm); +void SYNC_enable_dcc_busy(); +void SYNC_enable_dcc_ramfull(); + +// trigger ("hit" level) configuration +void TRIG_select_thresh_0(); +void TRIG_select_thresh_1(); +void TRIG_enable(); +void TRIG_disable(); + +// counters (time, events) +void CNT_reset(); +void CNT_start(); +void CNT_stop(); +alt_u32 CNT_hit0(int rfm); +alt_u32 CNT_hit1(int rfm); +alt_u32 CNT_trig(); +alt_u32 CNT_bcid24(); +alt_u32 CNT_bcid48msb(); +alt_u32 CNT_bcid48lsb(); +void CNT_save(FILE *output, int rfm); + +// Hardroc slow control registers +void HRSC_select_slow_control(); +void HRSC_select_read_register(); +void HRSC_set_bit(alt_u32 hr_addr, alt_u32 bit_addr, alt_u32 bit); +alt_u32 HRSC_get_bit(alt_u32 hr_addr, alt_u32 bit_addr); +void HRSC_set_word(alt_u32 hr_addr, alt_u32 bit_addr, alt_u32 n_bits, + alt_u32 value); +void HRSC_set_word_msb2lsb(alt_u32 hr_addr, alt_u32 bit_addr, alt_u32 n_bits, + alt_u32 value); +int HRSC_read_conf_singl(FILE *file_conf, alt_u32 hr_addr); +void HRSC_copy_conf(alt_u32 hr_addr_source, alt_u32 hr_addr_dest); +int HRSC_read_conf_mult(FILE *file_conf); +int HRSC_write_conf_mult(FILE *file_conf); +void HRSC_set_ctest(alt_u32 hr_addr, alt_u32 chan, alt_u32 val); +void HRSC_set_all_ctest_off(); +void HRSC_set_preamp(alt_u32 hr_addr, alt_u32 chan, alt_u32 val); +void HRSC_set_shaper_capa(alt_u32 hr_addr, alt_u32 val); +void HRSC_set_shaper_resis(alt_u32 hr_addr, alt_u32 val); +void HRSC_set_cmd_fsb2(alt_u32 hr_addr, alt_u32 chan, alt_u32 val); +void HRSC_set_cmd_fsb1(alt_u32 hr_addr, alt_u32 chan, alt_u32 val); +void HRSC_set_mask(alt_u32 hr_addr, alt_u32 chan, alt_u32 val); +void HRSC_set_chip_id(alt_u32 hr_addr, alt_u32 val); +void HRSC_set_DAC0(alt_u32 hr_addr, alt_u32 val); +void HRSC_set_DAC1(alt_u32 hr_addr, alt_u32 val); +void HRSC_set_DAC2(alt_u32 hr_addr, alt_u32 val); +void HRSC_set_DAC_coarse(alt_u32 hr_addr); +void HRSC_set_DAC_fine(alt_u32 hr_addr); +int HRSC_set_config(int rfm); +int HRSC_reset_read_registers(int rfm); +int HRSC_set_read_register(int rfm, int channel); +void HRSC_reset_sc(); +void HRSC_start_sc(int rfm); +int HRSC_sc_done(int rfm); + +// Hardroc DAQ +alt_u32 bit(alt_u32 word, alt_u32 digit); +void DAQ_fifo_init(int rfm); +void DAQ_fifo_print_status(int rfm); +void DAQ_fifo_print_event(int rfm); +void DAQ_fifo_clear_event(int rfm); +int DAQ_fifo_in_valid(int rfm); +int DAQ_fifo_full(int rfm); +int DAQ_fifo_empty(int rfm); +alt_u32 DAQ_fifo_fill_level(int rfm); +alt_u32 DAQ_fifo_data(int rfm); +alt_u32 DAQ_fifo_clear(int rfm); +alt_u32 DAQ_hexdump_hr_data(FILE *output, int rfm); +alt_u32 DAQ_save_hr_data(FILE *output, int rfm); +alt_u32 DAQ_save_hr_data_DIF(FILE *output, int rfm); +void DAQ_bufferize_data_DIF(int rfm); +int DAQ_send_sock_buffer(int sock); +int DAQ_write_buffer(FILE *f); +void DAQ_reset_buffer(); + +// Test injector +int INJ_dac_select(int rfm); +int INJ_dac_set(alt_u32 value); +void INJ_start_ctest_pulser(alt_u32 half_period, alt_u32 phase); +// half period unit = 200 ns +// phase unit = 6.25 ns +void INJ_stop_ctest_pulser(); + +// Printing +void PRINT_fifo_status(int rfm); +void PRINT_counters(FILE *output, int rfm); +void PRINT_hr_data(FILE *output, alt_u32 nb_evt); +void PRINT_config(FILE *file_conf, int rfm); +void PRINT_dump_registers(FILE *output); + +#endif /* !EDA_FPGA_H */ diff --git a/eda/io.h b/eda/io.h new file mode 100644 index 0000000..4563842 --- /dev/null +++ b/eda/io.h @@ -0,0 +1,91 @@ +#ifndef EDA_IO_H +#define EDA_IO_H 1 + +/****************************************************************************** + * * + * License Agreement * + * * + * Copyright (c) 2009 Altera Corporation, San Jose, California, USA. * + * All rights reserved. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * * + * This agreement shall be governed in all respects by the laws of the State * + * of California and by the laws of the United States of America. * + * * + * Altera does not recommend, suggest or require that this reference design * + * file be used in conjunction or combination with any other product. * + ******************************************************************************/ + +/* IO Header file for MIPS32 Toolchain */ + +#include "alt_types.h" +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef SYSTEM_BUS_WIDTH +#define SYSTEM_BUS_WIDTH 32 +#endif + +/* + * Converts a KSEG0 virtual address (Kernel unmapped cached) to a + * KSEG1 virtual address (Kernel unmapped uncached). + * Used to ensure that a virtual address will be accessed uncached. + */ +#define __KSEG0_TO_KSEG1(ADDR) ((ADDR) | 0xa0000000) +#define UNCACHEABLE_ADDR(ADDR) (__KSEG0_TO_KSEG1(ADDR)) + +/* Dynamic bus access functions */ + +#define __IO_CALC_ADDRESS_DYNAMIC(BASE, OFFSET) \ + ((void *)(__KSEG0_TO_KSEG1((alt_u32)BASE) + (OFFSET))) + +#define IORD_32DIRECT(BASE, OFFSET) \ + (*((volatile alt_u32 *)(__IO_CALC_ADDRESS_DYNAMIC((BASE), (OFFSET))))) +#define IORD_16DIRECT(BASE, OFFSET) \ + (*((volatile alt_u16 *)(__IO_CALC_ADDRESS_DYNAMIC((BASE), (OFFSET))))) +#define IORD_8DIRECT(BASE, OFFSET) \ + (*((volatile alt_u8 *)(__IO_CALC_ADDRESS_DYNAMIC((BASE), (OFFSET))))) + +#define IOWR_32DIRECT(BASE, OFFSET, DATA) \ + (*((volatile alt_u32 *)(__IO_CALC_ADDRESS_DYNAMIC((BASE), (OFFSET)))) = \ + (DATA)) +#define IOWR_16DIRECT(BASE, OFFSET, DATA) \ + (*((volatile alt_u16 *)(__IO_CALC_ADDRESS_DYNAMIC((BASE), (OFFSET)))) = \ + (DATA)) +#define IOWR_8DIRECT(BASE, OFFSET, DATA) \ + (*((volatile alt_u8 *)(__IO_CALC_ADDRESS_DYNAMIC((BASE), (OFFSET)))) = (DATA)) + +/* Native bus access functions */ + +#define __IO_CALC_ADDRESS_NATIVE(BASE, REGNUM) \ + ((void *)(__KSEG0_TO_KSEG1((alt_u32)BASE) + \ + ((REGNUM) * (SYSTEM_BUS_WIDTH / 8)))) + +#define IORD(BASE, REGNUM) \ + (*((volatile alt_u32 *)(__IO_CALC_ADDRESS_NATIVE((BASE), (REGNUM))))) +#define IOWR(BASE, REGNUM, DATA) \ + (*((volatile alt_u32 *)(__IO_CALC_ADDRESS_NATIVE((BASE), (REGNUM)))) = (DATA)) + +#ifdef __cplusplus +} +#endif + +#endif /* !EDA_IO_H */ diff --git a/eda/logger.c b/eda/logger.c new file mode 100644 index 0000000..d2933ef --- /dev/null +++ b/eda/logger.c @@ -0,0 +1,39 @@ +// Copyright 2021 The tomuvol-daq Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "logger.h" + +#include +#include + +// fixed-location, temporary, line-buffered log file +FILE *logfile; +char logbuf[128]; + +void log_init() { + logfile = fopen("/dev/shm/log.out", "w"); + memset(logbuf, '\0', 128); + setvbuf(logfile, logbuf, _IOLBF, 128); + return; +} + +void log_printf(const char *fmt, ...) { + va_list arglist; + va_start(arglist, fmt); + vsprintf(logbuf, fmt, arglist); + va_end(arglist); +} + +void log_flush() { + fprintf(stderr, "%s", logbuf); + fprintf(logfile, "%s", logbuf); + memset(logbuf, '\0', 128); + return; +} + +void log_close() { + log_flush(); + fclose(logfile); + return; +} diff --git a/eda/logger.h b/eda/logger.h new file mode 100644 index 0000000..63799b8 --- /dev/null +++ b/eda/logger.h @@ -0,0 +1,15 @@ +#ifndef EDA_LOGGER_H +#define EDA_LOGGER_H 1 + +// Copyright 2021 The tomuvol-daq Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include + +void log_init(); +void log_flush(); +void log_printf(const char *fmt, ...); +void log_close(); + +#endif // !EDA_LOGGER_H diff --git a/eda/server.go b/eda/server.go index 52ce393..4da8f27 100644 --- a/eda/server.go +++ b/eda/server.go @@ -57,7 +57,7 @@ func newServer(addr, odir, devmem, devshm string, opts ...Option) (*server, erro devshm: devshm, newDevice: func(devmem, odir, devshm string, opts ...Option) (device, error) { - return newDevice(devmem, odir, devshm, opts...) + return newCDevice(devmem, odir, devshm, opts...) }, opts: opts,