/*
 * DDR memory structures common to ARM and PRU
 * Only used for memory emulation, PRU read access to ARM DDR maybe upto 400ns !
 */

#ifndef _DDRMEM_H_
#define _DDRMEM_H_

#include <stdint.h>

#include "qunibus.h"

/***** start of shared structs *****/
// on PRU. all struct are byte-packed, no "#pragma pack" there
// (support answer 20.5.2018,  issue CODEGEN-4832)
#ifdef ARM
#pragma pack(push,1)
#endif

typedef struct {
	// DDR mem is only used to emulate QBUS/UNIBUS memory
	// This slow DDR RAM is not used otherwise
	qunibus_memory_t memory;
} ddrmem_t;

#ifdef ARM
#pragma pack(pop)
#endif
/***** end of shared structs *****/

#ifdef ARM
// included by ARM code

#include "logsource.hpp"

class ddrmem_c: public logsource_c {
public:
	/* these values are generated by prussdrv functions */
	// base address of shared DDR memory, in ARM Linux memory space
	volatile ddrmem_t *base_virtual;
	uint32_t len;	// size of allocated range as given by UIO driver
	// physical ddrmem_base address, for access by PRU
	uint32_t base_physical;

	// emulated address range
	bool enabled = false; // true if startaddr <= endaddr
	uint32_t qunibus_startaddr;
	uint32_t qunibus_endaddr; 

	uint32_t pmi_address_overlay ;

	ddrmem_c();
	void info(void);
	void save(char *fname);
	void load(char *fname);
	void clear(void);
	void fill_pattern(void);
	void fill_pattern_pru(void);
	void unibus_slave(uint32_t startaddr, uint32_t endaddr);
	bool set_range(uint32_t startaddr, 	uint32_t endaddr);
	bool deposit(uint32_t addr, uint16_t w);
	bool exam(uint32_t addr, uint16_t *w);

	bool pmi_deposit(uint32_t addr, uint16_t w);
	bool pmi_exam(uint32_t addr, uint16_t *w);
	void set_pmi_address_overlay(uint32_t address_overlay) ;
		
	bool iopage_deposit(uint32_t addr, uint16_t w) ;
	bool iopage_exam(uint32_t addr, uint16_t *w) ;
	
};

#ifndef _DDRMEM_C_
// base address of shared DDR memory, in ARM Linux memory space
extern ddrmem_c *ddrmem;
#endif

#else
// included by PRU code
// set a word in simulated memory
#define DDRMEM_MEMSET_W(addr,dataw) \
    	( mailbox.ddrmem_base_physical->memory.words[(addr)/2] = (dataw) )
// set a byte in simulated memory
#define DDRMEM_MEMSET_B(addr,datab) \
    	( mailbox.ddrmem_base_physical->memory.bytes[(addr)] = (datab) )

// return a word from simulated memory
#define DDRMEM_MEMGET_W(addr) \
    	( mailbox.ddrmem_base_physical->memory.words[(addr)/2] )

void ddrmem_fill_pattern(void);

#endif

#endif
