From f33d1e25cd595a0dcabeb4201a047fb89445b0ef Mon Sep 17 00:00:00 2001
From: tjames <tom.james@cern.ch>
Date: Mon, 16 Aug 2021 15:30:13 +0200
Subject: [PATCH] first MicronDAQ commit

---
 src/Makefile     |  15 +--
 src/config.h     |   5 +-
 src/micronDAQ.cc | 264 +++++++++++++++++++++++++++++++++++++++++++++++
 src/micronDAQ.h  |  33 ++++++
 src/scdaq.cc     |  11 +-
 5 files changed, 319 insertions(+), 9 deletions(-)
 create mode 100644 src/micronDAQ.cc
 create mode 100644 src/micronDAQ.h

diff --git a/src/Makefile b/src/Makefile
index fb18e466..d2d7a35f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -7,12 +7,14 @@
 # - if you need to pass flags to the linker, set LDFLAGS rather
 #   than alter the ${TARGET}: ${OBJECTS} rule
 # - to see make's built-in rules use make -p
-
 # target executable name
 TARGET = scdaq
-
+CXXFLAGS = -std=c++11 -Wall -Wextra -g -rdynamic -O3 -Wno-multichar -DLINUX -DPOSIX -I. -I${PICOBASE}/software/include/linux -I${PICOBASE}/software/include -fmessage-length=0 -fdiagnostics-show-option -fms-extensions -Wno-write-strings -DOSNAME="Linux" 
+#-o micronDAQ micronDAQ.h ${PICOBASE}/software/source/GString.cpp ${PICOBASE}/software/source/pico_drv.cpp ${PICOBASE}/software/source/pico_errors.cpp ${PICOBASE}/software/source/linux/linux.cpp ${PICOBASE}/software/source/pico_drv_linux.cpp
+#USER_SOURCES = micronDAQ.cc
+#include $(PICOBASE)/software/Makefile.common
 # source files
-SOURCES = config.cc dma_input.cc elastico.cc FileDmaInputFilter.cc file_input.cc  InputFilter.cc output.cc  processor.cc  scdaq.cc  session.cc  slice.cc WZDmaInputFilter.cc
+SOURCES = micronDAQ.cc config.cc dma_input.cc elastico.cc FileDmaInputFilter.cc file_input.cc  InputFilter.cc output.cc  processor.cc  scdaq.cc  session.cc  slice.cc WZDmaInputFilter.cc
 C_SOURCES = wz_dma.c
 
 # work out names of object files from sources
@@ -23,13 +25,13 @@ OBJECTS += $(C_SOURCES:.c=.o)
 # appropriate rules; CXXFLAGS gets passed as part of command
 # invocation for both compilation (where -c is needed) and linking
 # (where it's not.)
-CXXFLAGS = -std=c++11 -Wall -Wextra -O0 -g -rdynamic
+#CXXFLAGS = -std=c++11 -Wall -Wextra -O0 -g -rdynamic
 #CXXFLAGS = -std=c++11 -Wall -Wextra -g -rdynamic
 
 CFLAGS = $(CXXFLAGS)
 LDFLAGS = -ltbb -lboost_thread -lcurl
 
-CPPFLAGS = -I. -Iwzdma
+CPPFLAGS = -I. -Iwzdma -I${PICOBASE}/software/include -I${PICOBASE}/software/include/linux -I${PICOBASE}/software/source -DLINUX -DPOSIX
 
 # default target (to build all)
 all: ${TARGET}
@@ -57,7 +59,8 @@ ${TARGET}: ${OBJECTS}
 
 #test2.o : product.h test2.h
 
-scdaq.o:	file_input.h processor.h elastico.h output.h format.h server.h controls.h config.h session.h log.h
+scdaq.o:	file_input.h processor.h elastico.h output.h format.h server.h controls.h config.h session.h log.h 
+micronDAQ.o: micronDAQ.h ${PICOBASE}/software/source/GString.cpp ${PICOBASE}/software/include/pico_drv.h ${PICOBASE}/software/include/pico_errors.h ${PICOBASE}/software/include/linux/linux.h ${PICOBASE}/software/include/pico_drv_linux.h
 config.o:	config.h log.h
 dma_input.o:	dma_input.h slice.h
 elastico.o:	elastico.h format.h slice.h controls.h log.h
diff --git a/src/config.h b/src/config.h
index a98724cb..ca4a5037 100644
--- a/src/config.h
+++ b/src/config.h
@@ -10,7 +10,7 @@
 class config{
 public:
   
-  enum class InputType { WZDMA, DMA, FILEDMA, FILE };
+  enum class InputType { WZDMA, DMA, FILEDMA, MICRONDAQ, FILE };
 
   config(std::string filename);
 
@@ -27,6 +27,9 @@ public:
     if (input == "filedma") {
       return InputType::FILEDMA;
     }    
+    if (input == "micronDAQ") {
+      return InputType::MICRONDAQ;
+    }    
     if (input == "file") {
       return InputType::FILE;
     }
diff --git a/src/micronDAQ.cc b/src/micronDAQ.cc
new file mode 100644
index 00000000..21e33add
--- /dev/null
+++ b/src/micronDAQ.cc
@@ -0,0 +1,264 @@
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <picodrv.h>
+#include <pico_errors.h>
+#include <micronDAQ.h>
+
+
+
+// add pad bytes to next multiple of 16 bytes
+int micronDAQ::pad_for_16bytes(int len) {
+	int pad_len = len;
+	if(len%16 !=0) {
+		pad_len = len + (16 - len%16); 
+	}
+	return pad_len;
+}
+
+// print <count> 128-bit numbers from buf
+void micronDAQ::print128(FILE *f, void *buf, int count)
+{
+	uint32_t	*u32p = (uint32_t*) buf;
+
+	for (int i=0; i < count; ++i)
+		fprintf(f, "0x%08x_%08x_%08x_%08x\n", u32p[4*i+3], u32p[4*i+2], u32p[4*i+1], u32p[4*i]);
+}
+
+int micronDAQ::runMicronDAQ(int argc, char* argv[])
+{
+	int         err, i, j, stream1, stream2;
+	uint32_t    *rbuf, *wbuf, u32, addr;
+	size_t	size;
+	char        ibuf[1024];
+	PicoDrv     *pico;
+	const char* bitFileName;
+	const char* load_bitfile;
+	// specify the .bit file name on the command line
+	if (argc < 2) {
+		fprintf(stderr, "%s: Please specify the .bit file on the command line.\n"
+				"For example: StreamLoopback128 ../firmware/M505_LX325T_StreamLoopback128.bit\n", argv[0]);
+		exit(-1);
+	}
+	bitFileName = argv[1];
+	load_bitfile = argv[2];
+
+	printf("load", load_bitfile);	
+	// The RunBitFile function will locate a Pico card that can run the given bit file, and is not already
+	//   opened in exclusive-access mode by another program. It requests exclusive access to the Pico card
+	//   so no other programs will try to reuse the card and interfere with us.
+	printf("Loading FPGA with '%s' ...\n", bitFileName);
+	//	if(load_bitfile){ 
+	//	 err = RunBitFile(bitFileName, &pico);
+	//			} else{
+	err = FindPico(0x852, &pico);
+	//			}   
+
+
+
+	if (err < 0) {
+		// We use the PicoErrors_FullError function to decipher error codes from RunBitFile.
+		// This is more informative than just printing the numeric code, since it can report the name of a
+		//   file that wasn't found, for example.
+		fprintf(stderr, "%s: RunBitFile error: %s\n", argv[0], PicoErrors_FullError(err, ibuf, sizeof(ibuf)));
+		exit(-1);
+	}
+
+	// Data goes out to the FPGA on WriteStream ID 1 and comes back to the host on ReadStream ID 1
+	// the following function call (CreateStream) opens stream ID 1
+	printf("Opening streams to and from the FPGA\n");
+	stream1 = pico->CreateStream(1);
+	stream2 = pico->CreateStream(2);
+	if (stream1 < 0) {
+		// All functions in the Pico API return an error code.  If that code is < 0, then you should use
+		// the PicoErrors_FullError function to decode the error message.
+		fprintf(stderr, "%s: CreateStream error: %s\n", argv[0], PicoErrors_FullError(stream1, ibuf, sizeof(ibuf)));
+		//    fprintf(stderr, "%s: CreateStream error: %s\n", argv[0], PicoErrors_FullError(stream2, ibuf, sizeof(ibuf)));
+		exit(-1);
+	}
+	if (stream2 < 0) {
+		// All functions in the Pico API return an error code.  If that code is < 0, then you should use
+		// the PicoErrors_FullError function to decode the error message.
+		fprintf(stderr, "%s: CreateStream error: %s\n", argv[0], PicoErrors_FullError(stream2, ibuf, sizeof(ibuf)));
+		//    fprintf(stderr, "%s: CreateStream error: %s\n", argv[0], PicoErrors_FullError(stream2, ibuf, sizeof(ibuf)));
+		exit(-1);
+	}
+
+	// Pico streams come in two flavors: 4Byte wide, 16Byte wide (equivalent to 32bit, 128bit respectively)
+	// However, all calls to ReadStream and WriteStream must be 16Byte aligned (even for 4B wide streams)
+	//
+	// Now allocate 16B aligned space for read and write stream buffers
+	//
+	// Similarily, the size of the call, in bytes, must also be a multiple of 16Bytes. So check and pad
+	// to next 16Byte multiple
+	size = 1024 * sizeof(uint32_t);
+	//size = 16384 * sizeof(uint32_t);
+	size = pad_for_16bytes(size);
+
+	if (malloc) {
+		err = posix_memalign((void**)&wbuf, 16, size);
+		if (wbuf == NULL || err) {
+			fprintf(stderr, "%s: posix_memalign could not allocate array of %zd bytes for write buffer\n", argv[0], size);
+			exit(-1);
+		}
+		err = posix_memalign((void**)&rbuf, 16, size);
+		if (rbuf == NULL || err) {
+			fprintf(stderr, "%s: posix_memalign could not allocate array of %zd bytes for read buffer\n", argv[0], size);
+			exit(-1);
+		}
+	} else {
+		printf("%s: malloc failed\n", argv[0]);
+		exit(-1);
+	}
+
+	// fill the buffer with data we'll recognize when it comes back.
+	/*for (i=0; i < size/sizeof(wbuf[0]); ++i)
+	  wbuf[i] = 0x42000000 | i;
+	  */
+	// Here is where we actually call WriteStream
+	// This writes "size" number of bytes from our buffer (wbuf) to the stream specified by our stream handle (e.g. 'stream')
+	// This call will block until it is able to write the entire "size" Bytes of data.
+	/*printf("Writing %zd B\n", size);
+	  err = pico->WriteStream(stream, wbuf, size);
+	  if (err < 0) {
+	  fprintf(stderr, "%s: WriteStream error: %s\n", argv[0], PicoErrors_FullError(err, ibuf, sizeof(ibuf)));
+	  exit(-1);
+	  }*/
+
+	// clear our read buffer to prepare for the read.
+	memset(rbuf, 0, sizeof(rbuf));
+
+	// After we wrote some data to the FPGA, we expect the FPGA to operate upon that data and place
+	// some results into an output stream.
+	//
+	// As with WriteStream, a ReadStream will block until all data is returned from the FPGA to the host.
+	//
+	// A user application will either have a deterministic amount of results, or a non-deterministic amount. 
+	// - When a non-deterministic amount of results are expected, and given the blocking nature of the ReadStream,
+	//   a user should use the GetBytesAvailable() call to determine the amount of data available for retrieval.
+	// - When a deterministic amount of results is expected, a user can skip the performance impacting
+	//   GetBytesAvailable() call and request the full amount of results. The user could also call ReadStream()
+	//   iteratively, without GetBytesAvailable(), in which case getting smaller chunks of results may allow
+	//   additional processing of the data on the host while the FPGA generates more results. 
+	//   Either approach works, and should be kept in mind when tuning performance of your application.
+	//
+	// In the example below we use the GetBytesAvailable() call as an example. However since the StreamLoopback 
+	// firmware returns exactly 1 piece of output data for every 1 piece of input data that it receives this
+	// is not really necessary.
+	//
+	// All we have done thus far (after the call to WriteStream) is to clear out a buffer.
+	// However, by calling GetBytesAvailable, we will see that the FPGA has already processed some of that 
+	// data and placed it into an output stream.
+	i = pico->GetBytesAvailable(stream1, true /* reading */);
+	j = pico->GetBytesAvailable(stream2, true /* reading */);
+	if (i < 0){
+		fprintf(stderr, "%s: GetBytesAvailable error: %s\n", argv[0], PicoErrors_FullError(i, ibuf, sizeof(ibuf)));
+		exit(-1);
+	}
+	if (j < 0){
+		fprintf(stderr, "%s: GetBytesAvailable error: %s\n", argv[0], PicoErrors_FullError(j, ibuf, sizeof(ibuf)));
+		exit(-1);
+	}
+	printf("%d Bytes are available to read from the FPGA: stream 1 .\n", i);
+	printf("%d Bytes are available to read from the FPGA: stream 2 .\n", j);
+
+	// Since the StreamLoopback firmware echoes 1 piece of data back to the software for every piece of data
+	// that the software writes to it, we know that we can eventually read exactly the amount of data that
+	// was written to the FPGA.
+
+	// Here is where we actually call ReadStream
+	// This reads "size" number of bytes of data from the output stream specified by our stream handle (e.g. 'stream') 
+	// into our host buffer (rbuf)
+	// This call will block until it is able to read the entire "size" Bytes of data.
+	//size=pad_for_16bytes(pico->GetBytesAvailable(stream1, true)) - 16;
+	printf("Reading stream 1 %zd B\n", size);
+
+	err = pico->ReadStream(stream2, rbuf, size);
+	if (err < 0) {
+		fprintf(stderr, "%s: ReadStream error: %s\n", argv[0], PicoErrors_FullError(err, ibuf, sizeof(ibuf)));
+		exit(-1);
+	}
+
+	//size=pad_for_16bytes(pico->GetBytesAvailable(stream2, true)) - 16;
+	/*printf("Reading stream 2 %zd B\n", size);
+	  err = pico->ReadStream(stream2, rbuf, size);
+	  if (err < 0) {
+	  fprintf(stderr, "%s: ReadStream error: %s\n", argv[0], PicoErrors_FullError(err, ibuf, sizeof(ibuf)));
+	  exit(-1);
+	  }*/
+
+	//i = 512;
+	// Now that we have received all of our read data back from the FPGA, we print out the first 
+	// 512 bytes for the user.
+	// In this sample, it makes the most sense to look at this read data 16B at a time, so we print out 
+	// 16B chunks
+	printf("Data received back from FPGA:\n");
+	print128(stdout, rbuf, i/16);
+	// streams are automatically closed when the PicoDrv object is destroyed, or on program termination, but
+	//  we can also close a stream manually.
+	pico->CloseStream(stream1);
+	pico->CloseStream(stream2);
+
+	// verify the received data
+	int exp, run_total = 0;
+	for (i=0; i < (size / (sizeof(uint32_t))) ; i++) {
+		switch ( i % 4 ) {
+			case 0 : exp = wbuf[i] ; break ;
+			case 1 : run_total = run_total + wbuf[i-1] ; exp = run_total ; break ;
+			case 2 : exp = 0xdeadbeef ; break ;
+			default : exp = 0x42424242 ; break ;
+		}
+		/*	if (rbuf[i] != exp) {
+			printf("%s: Error: unexpected values found at rbuf[%d] %x != expected %x\n", argv[0], i, rbuf[i], exp);
+			exit(-1);
+			}*/
+	}
+
+	printf("All tests successful!\n");
+
+	//free(wbuf);
+	//free(rbuf);
+
+	exit(0);
+}
+/**************************************************************************
+ *  * Entry points are here
+ *   * Overriding virtual functions
+ *    */
+
+
+//Print some additional info
+void micronDAQ::print(std::ostream& out) const
+{
+/*	out
+		<< ", DMA errors " << stats.nbDmaErrors
+		<< ", oversized " << stats.nbDmaOversizedPackets
+		<< ", resets " << stats.nbBoardResets;
+*/
+}
+
+
+// Read a packet from DMA
+ssize_t micronDAQ::readInput(char **buffer, size_t bufferSize)
+{
+	// We need at least 1MB buffer
+	assert( bufferSize >= 1024*1024 );
+
+	//return WZDmaInputFilter::read_packet( buffer, bufferSize );
+	//temporary hack
+	return 1;
+}
+
+
+// Notifi the DMA that packet was processed
+void micronDAQ::readComplete(char *buffer) {
+/*	(void)(buffer);
+
+	// Free the DMA buffer
+	if ( wz_read_complete( &dma_ ) < 0 ) {
+		throw std::system_error(errno, std::system_category(), "Cannot complete WZ DMA read");
+	}
+*/
+}
diff --git a/src/micronDAQ.h b/src/micronDAQ.h
new file mode 100644
index 00000000..880f7b60
--- /dev/null
+++ b/src/micronDAQ.h
@@ -0,0 +1,33 @@
+#ifndef MICRONDAQ_H
+#define MICRONDAQ_H
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <pico_drv.h>
+#include <pico_errors.h>
+#include <stdint.h>
+#include <memory>
+#include <InputFilter.h>
+#include <WZDmaInputFilter.h>
+#include <cassert>
+class micronDAQ : public InputFilter {
+	public:
+		micronDAQ();
+		virtual ~micronDAQ();
+
+		int pad_for_16bytes(int);
+		void print128(FILE*, void*, int);
+		int runMicronDAQ(int, char**);
+	private:
+
+	protected:
+		ssize_t readInput(char **buffer, size_t bufferSize); // Override
+		void readComplete(char *buffer);  // Override
+		void print(std::ostream& out) const;  // Override
+};
+typedef std::shared_ptr<micronDAQ> micronDAQPtr;
+
+
+#endif
diff --git a/src/scdaq.cc b/src/scdaq.cc
index 8207e6f1..5baf15cd 100644
--- a/src/scdaq.cc
+++ b/src/scdaq.cc
@@ -18,6 +18,7 @@
 #include "InputFilter.h"
 #include "FileDmaInputFilter.h"
 #include "WZDmaInputFilter.h"
+#include "micronDAQ.h"
 #include "dma_input.h"
 #include "file_input.h"
 #include "processor.h"
@@ -28,6 +29,7 @@
 #include "controls.h"
 #include "config.h"
 #include "log.h"
+#include "micronDAQ.h"
 
 using namespace std;
 
@@ -70,9 +72,14 @@ int run_pipeline( int nbThreads, ctrl& control, config& conf )
       input_filter = std::make_shared<FileDmaInputFilter>( conf.getInputFile(), MAX_BYTES_PER_INPUT_SLICE, TOTAL_SLICES, control );
 
   } else if (input == config::InputType::WZDMA ) {
-      // Create WZ DMA reader
+      // create wz dma reader
       input_filter = std::make_shared<WZDmaInputFilter>( MAX_BYTES_PER_INPUT_SLICE, TOTAL_SLICES, control );
-
+  
+  } else if (input == config::InputType::MICRONDAQ ) {
+      // create micronDAQ reader
+      //input_filter = std::make_shared<micronDAQ>();
+      throw std::runtime_error("input type micronDAQ is a work in progress");
+	
   } else {
     throw std::invalid_argument("Configuration error: Unknown input type was specified");
   }
-- 
GitLab