How do I initialize an array at a specific address for CANbus RX/TX FIFO start address register (ATSAME51)?

94 Views Asked by At

The register for the CAN RX & TX buffer/FIFO start address (F0SA/TBSA) only consider the lower 16 bits of an address but a RAM address can span from 0x20000000 to 0x20040000 (18 bits).

RX FIFO Start Address Register TX Buffer Start Address Register

Link to datasheet.

So if I initialize an array to work as the RX FIFO like so:

uint8_t can1_rx_fifo0[CAN1_RX_FIFO_ELEMENTS * 16] __attribute__((__aligned__(4)));

And the compiler puts it at the memory address 0x20005A70, it's no problem as I can write 0x5A70 into F0SA/TBSA and CAN is going to see the correct buffer/FIFO address. But if that array gets put at address 0x20015A70, I can still only write 0x5A70 to F0SA/TBSA and this is the wrong address.

I tried doing it like this and the FIFO gets put at an appropriate address that I specified but then that address gets overwritten/used for other things in RAM.

#define CAN1_RX_FIFO            (volatile struct can1_rx_fifo_struct*)(0x20000000)

static struct can1_rx_fifo_struct
{
    uint8_t can1_rx_fifo[CAN1_RX_FIFO_ELEMENTS * 16];
};

Am I misunderstanding how this works? If not, how can I best and safely initialize the array so it gets stored at a memory address between 0x20000000 - 0x2000FFFF?

2

There are 2 best solutions below

0
Lundin On BEST ANSWER

When I wrote a CAN driver for another ATSAM part I just cheekily made sure it was allocated in .bss by making it static and then ensured that .bss was on top of the RAM.

From my driver code:

struct can_msg_t                   // opaque type used by the CAN driver API
{
  uint32_t reg0;
  uint32_t reg1;
  uint8_t  data [CAN_DATA_BYTES_N];
};
_Static_assert(sizeof(can_msg_t) == 8+CAN_DATA_BYTES_N,    // defensive programmming   
               "can_msg_t: incorrect padding detected");

...

typedef struct
{
  struct can_msg_t  rx_buffer     [CAN_RX_BUFFER_N];
  struct can_msg_t  rx_fifo_0     [CAN_RX_FIFO_N];
  struct can_msg_t  rx_fifo_1     [CAN_RX_FIFO_N];
  struct can_msg_t  tx_buffer     [CAN_TX_BUFFER_N + CAN_TX_FIFO_SIZE_N];
  can_msg_tx_event  tx_event_fifo [CAN_TX_EVENT_FIFO];
  uint32_t          rx_std_filter [CAN_RX_STD_FILTER_N];
  uint32_t          rx_ext_filter [CAN_RX_EXT_FILTER_N];
} can_port_t;

static volatile can_port_t can0;
static volatile can_port_t can1;

can0 and can1 will be allocated in .bss. And .bss isn't large enough to go out of the addressable range, and it was allocated at 0x20000000, so this will (cheekily) work.

You could also ensure that these aren't allocated in the wrong section by a static assert:

#define TOP_ADDRESS 0x2000FFFFu

_Static_assert( (uintptr_t)&can0 <= (TOP_ADDRESS - sizeof(can_port_t)), 
                "can0 outside addressable range for the CAN registers." );
_Static_assert( (uintptr_t)&can1 <= (TOP_ADDRESS - sizeof(can_port_t)), 
                "can1 outside addressable range for the CAN registers." );

Note that using structures also sorts out alignment automatically.

1
the busybee On

See chapter 39.9.1 of the linked datasheet:

Message RAM can only be located in the first 64 KB area of the system RAM.

Therefore, you need to make sure that your buffer is there.

Commonly you do this with the linker script and a specific section. Add the section as the first one before .data and .bss (and others) to the memory ram, or define your own memory space.