Teensy 4.1: How can I acess a continuous block of RAM of 800k bytes?

MichaelMC

Well-known member
Whilst developing firmware to drive a 800x480*2 display, I've hit upon a roadblock where the maximum allocation of a single block appears to be around 512k. This is understandable given the memory configuration of the Teensy4.1, but, is there any mechanism whereby a block larger than 512k may be seamlessly accessed?
 
Not from the two onboard RAM1 and RAM2 blocks of 512KB

If PSRAM at lower speed works it is contiguous 8MB or 16MB with chip(s) soldered in place on bottom QSPI pads.
 
FlexRAM (the 512KB region that is usually partitioned as ITCM and DTCM) can also be assigned as OCRAM, which will appear contiguously in memory after the other 512KB OCRAM. You'd probably have to change a few things in the linker script and startup code to get it working, and it would significantly reduce the space for code and tightly coupled data/stack space.
 
Thank you for the responses.

Jmarsh, that seems promising. However, as I plan on posting the project code here, as I believe others may find it interesting given the nature of the device, a requirement to modify the compiler environment could limit its reach. But something to test nonetheless.

I'll try PSRAM. In the mean time, I'll make do with a combination of 16bit colour through an 8bit colour-map (lookup table) and full 16bit colour through direct partial updates.
 
Just update, I found a working solution. I've split the screen memory over two buffers; top half 800x240 pixels occupied by a static RAM1 buffer, with the bottom RAM2 DMAMEM allocated buffer. But this therein arose a problem.
Whilst using a DMAMEM/alloc'd buffer (ie; RAM2) in combination with the USB display, there was continual corruption of the first 128'ish bytes of the transmitted DMAMEM buffer, irrespective of the actual buffer's memory location [in RAM2]. I was only able to resolve this corruption by calling arm_dcache_flush(buffer, len) after each memset() and USB buffer transmission.

Tried 2xPSRAM chips for 16MB, but this would eventually lock the teensy.

What this looks like is:
Code:
void sendBitmap (void *data, const int x, const int y, const int w, const int h)
{
	sbuiwriteop_t idesc;
	
	idesc.left = x;
	idesc.right = x + w-1;
	idesc.top = y;
	idesc.bottom = y + h-1;
	
	razersb_writeImage(&idesc, data);
}

const int ww = 800;
const int hh = 240;
uint32_t ct = 0;

#define len  (ww * hh * 2)
static uint8_t array8t[len];
static uint8_t DMAMEM array8b[len];


void loop ()
{
	usb_task();
	
	if (doTests){
		if (doTests == 1){
			doTests = 2;
			printf("%i %i %p %p\r\n", ww, hh, (void*)array8t, (void*)array8b);
		}
		
		if (++ct&0x01){
			memset(array8t, 0xFF, len);
			memset(array8b, 0xFF, len);
			arm_dcache_flush(array8b, len);
		}else{
			memset(array8t, 0x00, len);
			memset(array8b, 0x00, len);
			arm_dcache_flush(array8b, len);
		}

		sendBitmap(array8t, 0, 0, ww, hh);
		sendBitmap(array8b, 0, 240, ww, hh);
		arm_dcache_flush(array8b, len);

		delayTask(1000);

	}else{
		delay(30);
	}
}
 
For cases like this when the length is greater than the entire size of the data cache, wouldn't it be more useful to have a dcache_flush_all function that flushed all possible cache lines since it would be shorter?
 
Paul, No. DMA is only used within that snippet posted above. But, as the corruption could be a symptom of an issue caused elsewhere, eg; maxPacketSize of 512?, here is the razersb_writeImage() and the rest of the driver code from that:

Code:
//#include <Arduino.h>
#include <USBHost_t36.h>
#include "usbh_common.h"
#include "razersb.h"


static USBHost myusb;
static USBHub hub1(myusb);
static RazerSB razersb(myusb);



// #define print   Serial.println
#define println Serial.println


typedef struct _sb {
	Device_t *device;

	Pipe_t *txpipe;
	Pipe_t *rxpipe;
		
	uint16_t tx_size;		// wMaxPacketSize
	uint16_t rx_size;		// wMaxPacketSize
	
	uint8_t tx_ep;
	uint8_t rx_ep;
	
	uint8_t tx_ep_adr;
	uint8_t rx_ep_adr;
	
	int32_t pipeType;
	int32_t pipeDir;
	
	uint8_t rxpacket[4];
	
	USBDriver *driver;
}razerdev_t;

static razerdev_t razerdev[10];


#define LIBSBUI_EP_DISPLAY_PAD			0x01
#define LIBSBUI_EP_DISPLAY_KEYS			0x02
#define LIBSBUI_EP_IN_TOUCH				0x81
#define LIBSBUI_EP_IN_KEYS				0x82
#define LIBSBUI_EP_IN_DONTKNOW                  0x83

#define RAZERSB_INT_0					0
#define RAZERSB_INT_DISPLAY_PAD			1
#define RAZERSB_INT_DISPLAY_KEYS		2
#define RAZERSB_INT_INPUT_TOUCH			3
#define RAZERSB_INT_INPUT_KEYS			4




volatile static int klen = 0;
volatile static int tlen0 = 0;
volatile static int tlen = 0;





void usb_start ()
{
	myusb.begin();
}

void usb_task ()
{
	myusb.Task();
}

void razersb_setCallbackFunc (int (*func)(uint32_t msg, intptr_t *value1, uint32_t value2))
{
	razersb.callbackFunc = func;
}


void RazerSB::init ()
{
	lineLength = 0;
	start = 0;
	callbackFunc = NULL;
	
	//contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t));
	contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t));
	
	driver_ready_for_device(this);
}

void RazerSB::Task ()
{
	//println("RazerSB Task:  enum_state = ", device->enum_state);
	//Serial.printf("RazerSB Task:  enum_state %d\r\n", device->enum_state);
	
	if (device->enum_state == USBH_ENUMSTATE_END){	// 15
		if (!start){
			start = millis();
		}else if (millis() - start > 2500){	// display startup time
			device->enum_state++;
			driverReady();
		}
	}
}

void RazerSB::driverReady ()
{
	// println("RazerSB driverReady  = ", (uint32_t)this, HEX);
	
	if (callbackFunc)
		(*callbackFunc)(USBD_MSG_DEVICEREADY, NULL, 0);
}


// type 0: claim complete device
// type 1: claim per interface
bool RazerSB::claim (Device_t *dev, int type, const uint8_t *descriptors, uint32_t len)
{
	// only claim at interface level
	if (type != 1) return false;

	if (dev->idVendor != RAZERSB_VID || dev->idProduct != RAZERSB_PID){
		//println("  device is not a RazerSB");
		return false;
	}else{
		//println("  found a RazerSB");
	}

	device = dev;

	//Serial.printf("\r\n\r\n\r\n###############\r\nRazerSB claim this %p\r\n", (uint32_t)this);
	
	//Serial.printf("claimType = %d\r\n", type);
	//Serial.printf("idVendor = %X\r\n" , dev->idVendor);
	//Serial.printf("idProduct = %X\r\n" , dev->idProduct);


	const uint8_t *p = descriptors;
	const uint8_t *end = p + len;

	// http://www.beyondlogic.org/usbnutshell/usb5.shtml
	int descriptorLength = p[0];
	int descriptorType = p[1];
	
	//Serial.printf("descriptorType = %d\r\n", descriptorType);
	//Serial.printf("descriptorLength = %d\r\n",  descriptorLength);
	
	if (!descriptorLength){
		//Serial.printf("3\r\n");
		return false;
	}
	if (descriptorType != USBH_DESCRIPTORTYPE_INTERFACE /*|| descriptorLength != 9*/){
		//Serial.printf("2\r\n");
		return false;
	}

	//descriptor_interface_t *interface = (descriptor_interface_t*)&p[0];

	//Serial.printf("bInterfaceClass = %d\r\n", interface->bInterfaceClass);
	//Serial.printf("bInterfaceSubClass = %d\r\n",  interface->bInterfaceSubClass);
	
	//if (interface->bInterfaceClass != USBH_DEVICECLASS_VENDOR || interface->bInterfaceSubClass != 0)
	//	return false;

	//println("  Interface is RazerSB");
	
	p += descriptorLength;	// == sizeof(descriptor_interface_t)
	rx_ep = 0;
	tx_ep = 0;
	//int interfaceCt = 0;

	while (p < end){
		int interfaceLength = p[0];
		if (p + interfaceLength > end){
			//Serial.printf("1\r\n");
			return false; // reject if beyond end of data
		}

		int interfaceType = p[1];
		//Serial.printf(":: interfaceType = %d\r\n", interfaceType);

		if (interfaceType == USBH_DESCRIPTORTYPE_ENDPOINT){
	
			println(" ");
			//Serial.printf("interface number : %d\r\n", interfaceCt++);
			//Serial.printf("interfaceType = %d\r\n", interfaceType);
			//Serial.printf("interfaceLength = %d\r\n", interfaceLength);
			descriptor_endpoint_t *endpoint = (descriptor_endpoint_t*)&p[0];
			
			//Serial.printf("bEndpointAddress = %X\r\n", endpoint->bEndpointAddress);
			//Serial.printf("bmAttributes = %X\r\n", endpoint->bmAttributes);
			//Serial.printf("wMaxPacketSize = %d\r\n", endpoint->wMaxPacketSize);
			//Serial.printf("bInterval = %d\r\n", endpoint->bInterval);

			//const int pipeType = endpoint->bmAttributes&0x03;
			//const int pipeDir = (endpoint->bEndpointAddress&0x80) >> 7;
			
			// type: 0 = Control, 1 = Isochronous, 2 = Bulk, 3 = Interrupt
			//Serial.printf("  endpoint type : %d\r\n", pipeType);
			//Serial.printf("  endpoint dir  : %d\r\n", pipeDir);
			//Serial.printf("  endpoint addr : %X\r\n", endpoint->bEndpointAddress/*&0x0F*/);

			if (endpoint->bEndpointAddress == LIBSBUI_EP_DISPLAY_PAD && endpoint->wMaxPacketSize == 512){
				razerdev_t *rdev = &razerdev[RAZERSB_INT_DISPLAY_PAD];
				
				rdev->device = dev;
				rdev->tx_ep = endpoint->bEndpointAddress&0x0F;
				rdev->tx_size = endpoint->wMaxPacketSize;
				rdev->pipeType = endpoint->bmAttributes&0x03;
				rdev->pipeDir = (endpoint->bEndpointAddress&0x80) >> 7;
				
				if (!rdev->txpipe)
					rdev->txpipe = new_Pipe(rdev->device, rdev->pipeType, rdev->tx_ep, rdev->pipeDir, rdev->tx_size);
				
				//Serial.printf("###  pad rdev->txpipe = %p\r\n", rdev->txpipe);
				
				rdev->driver = this;
				
			}else if (endpoint->bEndpointAddress == LIBSBUI_EP_DISPLAY_KEYS && endpoint->wMaxPacketSize == 512){
				razerdev_t *rdev = &razerdev[RAZERSB_INT_DISPLAY_KEYS];
				
				rdev->device = dev;
				rdev->tx_ep = endpoint->bEndpointAddress&0x0F;
				rdev->tx_size = endpoint->wMaxPacketSize;
				rdev->pipeType = endpoint->bmAttributes&0x03;
				rdev->pipeDir = (endpoint->bEndpointAddress&0x80) >> 7;
				
				if (!rdev->txpipe)
					rdev->txpipe = new_Pipe(rdev->device, rdev->pipeType, rdev->tx_ep, rdev->pipeDir, rdev->tx_size);
					
				//Serial.printf("###  keys rdev->txpipe = %p\r\n", rdev->txpipe);
				rdev->driver = this;
				
			}
#if 0
			else if (endpoint->bEndpointAddress == LIBSBUI_EP_IN_TOUCH && endpoint->wMaxPacketSize == 64){
				razerdev_t *rdev = &razerdev[RAZERSB_INT_INPUT_TOUCH];
				rdev->device = dev;
				rdev->rx_ep = endpoint->bEndpointAddress&0x0F;
				rdev->rx_size = endpoint->wMaxPacketSize;
				rdev->pipeType = endpoint->bmAttributes&0x03;
				rdev->pipeDir = (endpoint->bEndpointAddress&0x80) >> 7;
				
				rdev->rxpipe = new_Pipe(rdev->device, rdev->pipeType, rdev->rx_ep, rdev->pipeDir, rdev->rx_size, endpoint->bInterval);
				if (rdev->rxpipe){
					rdev->rxpipe->callback_function = rx_callback_touch;
					queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, rdev->rx_size, this);
					rdev->driver = this;
				}
				
			}else if (endpoint->bEndpointAddress == LIBSBUI_EP_IN_KEYS){
				razerdev_t *rdev = &razerdev[RAZERSB_INT_INPUT_KEYS];
				
				rdev->device = dev;
				rdev->rx_ep = endpoint->bEndpointAddress&0x0F;
				rdev->rx_size = endpoint->wMaxPacketSize;
				rdev->pipeType = endpoint->bmAttributes&0x03;
				rdev->pipeDir = (endpoint->bEndpointAddress&0x80) >> 7;
				
				rdev->rxpipe = new_Pipe(rdev->device, rdev->pipeType, rdev->rx_ep, rdev->pipeDir, rdev->rx_size, endpoint->bInterval);
				if (rdev->rxpipe){
					rdev->rxpipe->callback_function = rx_callback_keys;
					//queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, rdev->rx_size, this);
					rdev->driver = this;
				}

			}else if (endpoint->bEndpointAddress == LIBSBUI_EP_IN_TOUCH && endpoint->wMaxPacketSize == 8){
				razerdev_t *rdev = &razerdev[RAZERSB_INT_0];
				
				rdev->device = dev;
				rdev->rx_ep = endpoint->bEndpointAddress&0x0F;
				rdev->rx_size = endpoint->wMaxPacketSize;
				rdev->pipeType = endpoint->bmAttributes&0x03;
				rdev->pipeDir = (endpoint->bEndpointAddress&0x80) >> 7;
				
				rdev->rxpipe = new_Pipe(rdev->device, rdev->pipeType, rdev->rx_ep, rdev->pipeDir, rdev->rx_size, endpoint->bInterval);
				if (rdev->rxpipe){
					rdev->rxpipe->callback_function = rx_callback_touch0;
					queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, rdev->rx_size, this);
					
					rdev->driver = this;
				}

			}else if (0){
				razerdev_t *rdev = &razerdev[interfaceCt];
				
				rdev->device = dev;
				rdev->rx_ep = endpoint->bEndpointAddress&0x0F;
				rdev->rx_size = endpoint->wMaxPacketSize;
				rdev->pipeType = endpoint->bmAttributes&0x03;
				rdev->pipeDir = (endpoint->bEndpointAddress&0x80) >> 7;
				
				rdev->rxpipe = new_Pipe(rdev->device, rdev->pipeType, rdev->rx_ep, rdev->pipeDir, rdev->rx_size, endpoint->bInterval);
				if (rdev->rxpipe){
					rdev->rxpipe->callback_function = rx_callback_touch;
					queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, rdev->rx_size, this);
					
					rdev->driver = this;
				}
			
		}
#endif
		}

		p += interfaceLength;
	}

	//Serial.printf("  endpoint txpipe : %d\r\n", (uint32_t)txpipe);
	//Serial.printf("  endpoint rxpipe  : %d\r\n", (uint32_t)rxpipe);
	//Serial.printf("***********************************************\r\n");

	return true;//(rxpipe || txpipe);
}



#if 0
static int tx_state = 0;


void RazerSB::tx_callback (const Transfer_t *transfer)
{
	//// println("RazerSB tx_callback");
	
	if (transfer->driver){
		if (tx_state == -1){
			//// println("RazerSB tx_callback tx_state ", tx_state);
			((RazerSB*)(transfer->driver))->drawScreenSetup();

		}else if (tx_state == 1){
			tx_descript_t *context = &((RazerSB*)(transfer->driver))->tx_writeCtx;
				
			int ret = (*((RazerSB*)(transfer->driver))->callbackFunc)(USBD_MSG_WRITEREADY, (intptr_t*)context, 0);
			if (ret){
				//printf("tx_callback %i %i\r\n", (int)context->frame, (int)context->row);
				
				((RazerSB*)(transfer->driver))->writeData(context->buffer, context->len);
				context->row += context->rows;
			}else{
				tx_state = 0;
				(*((RazerSB*)(transfer->driver))->callbackFunc)(USBD_MSG_WRITEEND, (intptr_t*)context, 0);
			}
		}
	}
}

#endif

/*
void dump (char *buffer, int len)
{
	for (int j = 0; j < len; j++)
		Serial.printf("%.2x ", buffer[j]);
	Serial.printf("\r\n");
}
*/

#if 0

void RazerSB::rx_callback_touch0 (const Transfer_t *transfer)
{
	//println("RazerSB rx_callback_touch0");
	
	if (transfer->driver){
		
		//((RazerSB *)(transfer->driver))->new_data(transfer);
		int32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
		
		Serial.printf("rx_callback_touch0 %d\r\n", len);
		//dump((char*)transfer->buffer, len);
		razerdev_t *rdev = &razerdev[RAZERSB_INT_0];
		queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, len, rdev->driver);
					
		if (len < 1 || len > 8){
			//razerdev_t *rdev = &razerdev[RAZERSB_INT_0];
			//queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, 8, rdev->driver);
			tlen0 = 0;
		}else{
			tlen0 = len;
		}
	}
}

void RazerSB::rx_callback_touch (const Transfer_t *transfer)
{
	//println("RazerSB rx_callback_touch");
	
	if (transfer->driver){
		
		//((RazerSB *)(transfer->driver))->new_data(transfer);
		int32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
		
		Serial.printf("rx_callback_touch %d\r\n", len);
		//dump((char*)transfer->buffer, len);
		razerdev_t *rdev = &razerdev[RAZERSB_INT_INPUT_TOUCH];
		queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, len, rdev->driver);
		
		if (len < 1 || len > 64){
			//razerdev_t *rdev = &razerdev[RAZERSB_INT_INPUT_TOUCH];
			//queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, 64, rdev->driver);
			tlen = 0;
		}else{
			tlen = len;
		}
	}
}

void RazerSB::rx_callback_keys (const Transfer_t *transfer)
{
	//println("RazerSB rx_callback_keys");
	
	if (transfer->driver){
		//((RazerSB *)(transfer->driver))->new_data(transfer);
		
		int32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
		Serial.printf("rx_callback_keys %d\r\n", len);
		//dump((char*)transfer->buffer, len);
		
		razerdev_t *rdev = &razerdev[RAZERSB_INT_INPUT_KEYS];
		queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, len, rdev->driver);

		if (len < 1 || len > 16){
			//razerdev_t *rdev = &razerdev[RAZERSB_INT_INPUT_KEYS];
			//queue_Data_Transfer(rdev->rxpipe, rdev->rxpacket, 16, rdev->driver);
			klen = 0;
		}else{
			klen = len;
		}
	}
}
#endif

void RazerSB::control (const Transfer_t *transfer)
{
	// println("RazerSB control()");
}

void RazerSB::disconnect ()
{
	// println("RazerSB disconnect()");
	
	// TODO: free resources
	device = NULL;
	start = 0;
	lineLength = 0;
}

bool RazerSB::usb_control_msg (Device_t *dev, uint32_t orequestType, uint32_t omsg, uint32_t ovalue, uint32_t oindex, void *buff, uint32_t len)
{
	setup_t setup;
	mk_setup(setup, orequestType, omsg, ovalue, oindex, len);
	return queue_Control_Transfer(dev, &setup, buff, NULL);
}

bool RazerSB::usb_bulk_write (USBDriver *drv, Pipe_t *pipe, const void *buffer, const uint32_t len)
{
	//printf("usb_bulk_write %i\r\n", len);
	return queue_Data_Transfer(pipe, (void*)buffer, len, drv);
}

int RazerSB::writeData (void *data, const size_t size)
{
	
	razerdev_t *rdev = &razerdev[RAZERSB_INT_DISPLAY_PAD];
	
	//__disable_irq();
	//NVIC_DISABLE_IRQ(IRQ_USBHS);
	int ret = usb_bulk_write(this, rdev->txpipe, data, size);
	//NVIC_ENABLE_IRQ(IRQ_USBHS);
	//__enable_irq();
	return ret;
}

volatile uint16_t swap16 (const uint16_t src)
{
	volatile unsigned char *tmp = (unsigned char*)&src;
	return (tmp[0] << 8 | tmp[1]);
}

int razersb_writeImage (sbuiwriteop_t *idesc, void *pixelData)
{
	return razersb.writeImage(idesc, pixelData);
}

// todo: int RazerSB::writeImage (sbuiwriteop_t *idesc, void *pixelData, flags)
int RazerSB::writeImage (sbuiwriteop_t *idesc, void *pixelData)
{
	uint16_t header[6];
	header[0] = swap16(1);
	header[1] = swap16(idesc->left);
	header[2] = swap16(idesc->top);
	header[3] = swap16(idesc->right);
	header[4] = swap16(idesc->bottom);
	header[5] = header[0] ^ header[1] ^ header[2] ^ header[3] ^ header[4];

	if (writeData((void*)header, sizeof(header))){
		delayMicroseconds(500);
		
		sbuiwriteop_t r = *idesc;
		const int len = ((r.right - r.left)+1) * ((r.bottom - r.top)+1) * 2;
		int ret = writeData((uint8_t*)pixelData, len);
		
		// ISV requires at least 18ms to complete the write otherwise jittering will occur
		// if your design does not allow successive calls within this period, then this delay may not be necessary 
		delay(19);		

		//arm_dcache_flush((void*)pixelData, len);	// shouldn't be here
		return (ret == len);
	}
	return 0;
}
 
Back
Top