Tool/software:
Hi,
In my linke command file I do this:
Tool/software:
Hi,
In my linke command file I do this:
In general, you can't assume that there is only one CRC_TABLE entry for .text:
1) The linker is capable of generating alignment "holes" such that some bytes are not predictable once loaded. For this case the linker generates multiple entries.
2) It's possible for .text to be split between FLASH and FLASH2. (This is improbable when giving preference to FLASH2, since it's so big, but it is possible.)
You should just step through the entries num_recs times.
-------
DMASA/DMADA are 32 bits, so they can accommodate addresses in FLASH2. To make sure the compiler gets the pointer sizes right, you should use the intrinsic "__data20_write_long" as illustrated in example msp430fr60x7_dma_01.c here:
https://dev.ti.com/tirex/explore/node?node=A__AJThw7eQLuzxGR4hTWjRfQ__msp430ware__IOGqZri__LATEST
I currently use the DMA_setSrcAddress from the TI Driver Lib in my Dma Feed policy. As far as I can see this implementation is than wrong and could be the trouble?
void DMA_setSrcAddress(uint8_t channelSelect, uint32_t srcAddress, uint16_t directionSelect)
{
// Set the Source Address
__data16_write_addr((unsigned short)(DMA_BASE + channelSelect + OFS_DMA0SA), srcAddress);
// Reset bits before setting them
HWREG16(DMA_BASE + channelSelect + OFS_DMA0CTL) &= ~(DMASRCINCR_3);
HWREG16(DMA_BASE + channelSelect + OFS_DMA0CTL) |= directionSelect;
}
void DMA_setDstAddress(uint8_t channelSelect, uint32_t dstAddress, uint16_t directionSelect)
{
// Set the Destination Address
__data16_write_addr((unsigned short)(DMA_BASE + channelSelect + OFS_DMA0DA), dstAddress);
// Reset bits before setting them
HWREG16(DMA_BASE + channelSelect + OFS_DMA0CTL) &= ~(DMADSTINCR_3);
HWREG16(DMA_BASE + channelSelect + OFS_DMA0CTL) |= (directionSelect << 2);
}Based on C compiler User Guide (SLAU132Y) Table 6-5, the "16" vs "20" refers to the width of the target (register) address, not the size of the datum (32 bits in both cases). On the FR6007 [Ref datasheet (SLASEV3A) Table 9-64], these registers start at about 0x0500, i.e. 16-bit addressable.
So while the data20 variant is perhaps a "good habit", I expect the data16 variants work fine.
Ok tried it. Unfortunately this was not the solution. Second problem I could not debug it because with dma debugger does not work.
Problem is still that my routine works so far. I added also a test with crc_table functionality in my component this also works.
The only difference I see is that in my product the FRAM is filled (more than 64KB) and in the stand alone component test not.
saved checksums from the linker script of ti are not the same to my calculated ones. Im writing byt by byte. you can ignore the type of crc it is a normal add functionality. What could be different
CRC_TABLE const *p_crc_table_text = &crc_table_text;
CRC_TABLE const *p_crc_table_isr_text = &crc_table_isr_text;
const uint32_t calculatedCrcText = crc_calc((uint8_t *)p_crc_table_text->recs[0].addr,
p_crc_table_text->recs[0].size, 0x00000000, false);
if (calculatedCrcText != p_crc_table_text->recs[0].crc_value)
{
// Error
}
I did some test in isolated crc component
some input
in general, as mentioned, the calculation worked. Unfortunately when I create a huge array to simulate a filled FRAM it seems that the linker do not place it propperly
I created an array:
__attribute__((section(".const"), used)) const std::array<uint8_t, 0x33000> fill_array{0x01}
in the linker command file I placed this:
.const : {} crc_table (crc_table_const, algorithm=CRC32_PRIME)>> FRAM | FRAM2 /* Constant data */
In the map file:

it is there and in the listing
you can see that it is on the higher FRAM but on the target you see that it has not two entries for high and low part. I really do not get it and even do not know how to simulate it to get the error. The calculated checksums fit (you can see in the pictures below) so calculation is correct so far. Should have somethinig to do with the high and low memory area in my opinion.

Your screenshot of the crc_tables indicates num_recs=2 but (as the debugger sees it) each has only one element. This is presumably from the "CRC_RECORD recs[1];" declaration in crc_tbl.h. You may have to resort to using the Memory Browser to see the other entry.
---------------
I've used the DMA on high-FRAM on an FR5994 before (I needed a large input buffer), and it worked as expected. That said, there are sporadic reports of the DMAxSA/DA not getting all the bits (mixtures of memory models and Errata), and the usual recommendation (as Mr Lehman states here) is to always use data20_write_long. (I don't know why the DriverLib people didn't.) It might be useful to examine the DMA0 registers (CTL/SA/DA/SZ) before and after the DMA operation.
so my assumption to see at lease 2 records for this linker command line is correct yes?
.text : {} crc_table (crc_table_text, algorithm=CRC32_PRIME)>> FRAM2 | FRAM
I would expect one for FRAM and one for FRAM2
You would see that in the case where FRAM2 filled up, which is not unlikely in this case (since you filled most of FRAM2 with that array).
Your screenshot says num_recs=2 (for both .text and .const), so apparently that's what happened.
If you have any functions declared as ISRs (#pragma vector=) they're put in something called ".text:_isr" which is put in low-FRAM. That area is (I think) not included in the .text CRC.
so my crc dma feed method looks like this but this kills the system
I have a lot of questions on this module and im really frustrated.
static void feed(const uint8_t *src, size_t length, const uint8_t *dst)
{
// Add size validation (your original 0x5FF is fine)
if (length > 0xFFFF || length == 0)
{
return; // Invalid size
}
DMA_initParam cfg = {
DMA_CHANNEL_0, // channelSelect
DMA_TRANSFER_BLOCK, // transferModeSelect
length, // transferSize
DMA_TRIGGERSOURCE_0, // triggerSourceSelect
DMA_SIZE_SRCBYTE_DSTBYTE, // transferUnitSelect
DMA_TRIGGER_RISINGEDGE // triggerTypeSelect
};
DMA_init(&cfg);
// Set direction bits
HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) &= ~(DMASRCINCR | DMADSTINCR);
HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) |= DMASRCINCR_3;
// Try without masking first
__data20_write_long((uintptr_t)&DMA0SA, (uintptr_t)src);
__data20_write_long((uintptr_t)&DMA0DA, (uintptr_t)dst);
// Clear flag and enable
DMA0CTL &= ~DMAIFG;
DMA_enableTransfers(DMA_CHANNEL_0);
DMA0CTL |= DMAREQ;
// Add timeout to debug
uint32_t timeout = 10000;
while (!(DMA0CTL & DMAIFG) && --timeout)
{
__no_operation();
}
if (timeout == 0)
{
// Timeout occurred - add breakpoint here for debugging
__no_operation();
}
DMA_disableTransfers(DMA_CHANNEL_0);
}
I want to calculate the crc over the code. Software is too slow so dma should be used.
from crc driver i give a chunk of memory (because dma is limited to 16bit length) to the feed function. with my settings I transfer this block of memory byte by byte to the crc destination address in one block. imagine the dst is the crc target register. FRom settings point the dma should just increment the src address not the destination address. When I add the line DMA0CTL |= DMAREQ the system crashes. I do not expect an interrupt. Is this line even needed? but when I remove this line I get a timeout from the memory movement. do you have a working example on using the dma with the crc module to calculate the crc in the 20bit memory area?
When I add the line DMA0CTL |= DMAREQ the system crashes. I do not expect an interrupt. Is this line even needed?
Of course it is required. You have to trigger the DMA or nothing happens.
Hmmm. I was just looking at the GCC version of __data20_write_long() and something is very wrong with it.
#define _data20_write_long(addr,src) \
({ \
unsigned int __tmp; \
unsigned long __addr = addr; \
__asm__ __volatile__ ( \
"movx.a %1, %0 \n\t" \
"mov.w %L2, 0(%0) \n\t" \
"mov.w %H2, 2(%0)" \
: "=&r"((unsigned int) __tmp) \
: "m"((unsigned long) __addr), "ri"((long) src) \
); \
})
After the expected movx.a instruction, there are two word writes. Which completely destroys the intent and zeros out the upper word when used with DMA address registers. In the past there was a __data20_write_addr() that didn't have this redundant and faulty write.
Oh, I suspect that the system crash is a power up clear (PUC) from accessing vacant peripheral space.
Lots of ways to check this. Examining the generated code being the simplest.
i made a tiny test with the driverlib. I can see that with --data-model large the controller hangs up in frequency setup. when my dma setup is included, when i exclude the dma setupt it does not stuck in the crystal setup. It seems to work with -data-model restricted. but this is just a gues, I did not try all combinations.
Single mode requires a trigger for each transfer. Which would make this slower than not using DMA.
Burst block mode would be better if you needed to slip some CPU activity in while the transfer happens. Since you are blocking till it completes, that isn't the case.
Oh, I looked at dma.obj in that pile of code you included. I assume this code is from DriverLib. What I see should work fine for setting the source and destination DMA addresses.
hangs up in frequency setup.
Usually you clear LOCKLPM5 early. Not after configuring the LFXT pins and hoping that the general rule on GPIO configuration doesn't apply to the LFXT.
no its not from driverlib. there is no real example on using the dma in combination with crc module in the the higher address area. It is my example to show you that something strange happens when I run the example (even with PM5CTL0 &= ~LOCKLPM5; moved to Top (below the PJ port init) the crystal was never initialized). If I remove my dma code, it runs to the end.
Dont know whats happening here.
I don't have a device with CRC32 handy but I did run this code on the fr5969 with no trouble:
int main(void)
{
DMA0CTL = DMASBDB | DMASRCINCR_3 | DMADT_1;
DMA0DA = &CRCDIRB_H;
__data16_write_addr(&DMA0SA, 0x10000L);
DMA0SZ = 0xffff;
DMA0CTL |= DMAEN;
CRCINIRES = 0xffff;
DMA0CTL |= DMAREQ;
while (1);
}
with the 16 bit address range it also works on my side. the problem is with the 20bit address range
with the 16 bit address range it also works on my side. the problem is with the 20bit address range
Break it up into smaller parts.
I have to jump to higher addresses (20bit) with the dma independent from the chunk size, and it looks like, that this is the problem.
It shouldn't be. At least with GCC I get reasonable code and the expected behaviour.
$ /usr/ti/gcc/bin/msp430-elf-objdump -S a.out
__data16_write_addr(&DMA0SA, 0x10000L);
4468: 81 43 00 00 mov #0, 0(r1) ;r3 As==00
446c: 91 43 02 00 mov #1, 2(r1) ;r3 As==01
00004470 <.Loc.17.1>:
4470: 8c 00 12 05 mova #1298, r12 ;0x00512
4474: 00 18 ec 41 movx.a @r1, 0(r12) ;
4478: 00 00
unfortunately this does not help me. First I use CGT, second I use __data20_wirte. I also get correct assembly with __data20_write but the code stucks anyway when I add: DMA0CTL |= DMAREQ; (done after the clock init). when this line is uncommented. the crystal setup hangs in the while loop for whatever reason.
uint16_t volatile resetReason = 0;
int main()
{
resetReason = SYSRSTIV;
resetReason = 0;
WDTCTL = WDTPW | WDTHOLD; // Stop the watchdog timer
// To enable LFXT, the PSEL bits associated with the crystal pins must be set. - RM 3.2
// Setting the PSEL bit causes the LFXIN and LFXOUT ports to be configured for LFXT operation.
// LFXIN = PJ.4
PJSEL0 |= BIT4 | BIT5; // Set bits 4 and 5 in PJSEL0 to enable primary function
PJSEL1 &= ~(BIT4 | BIT5); // Clear bits 4 and 5 in PJSEL1 to select primary function
PM5CTL0 &= ~LOCKLPM5; // Clear the LOCKLPM5 bit to enable GPIO functionality
// Unlock CS registers
CSCTL0 = 0xA500; // Password to unlock clock system registers
// Set DCO to 8 MHz
CSCTL1 = 0x000C;
// Configure clock sources
CSCTL2 = SELM__DCOCLK | SELS__DCOCLK | SELA__LFXTCLK;
// MCLK = DCOCLK, SMCLK = DCOCLK, ACLK = LFXTCLK
// Set dividers for MCLK, SMCLK, and ACLK
CSCTL3 = DIVM__1 | DIVS__1 | DIVA__1;
// MCLK = DCO / 1, SMCLK = DCO / 1, ACLK = LFXT / 1
// Enable LFXT with drive strength 3
CSCTL4 &= ~LFXTOFF; // Enable LFXT
CSCTL4 |= LFXTDRIVE_3; // Set LFXT drive strength to 3
// Clear fault flags and wait for LFXT to stabilize
do
{
CSCTL5 &= ~LFXTOFFG; // Clear LFXT fault flag
SFRIFG1 &= ~OFIFG; // Clear oscillator fault flag
} while (SFRIFG1 & OFIFG);
// Lock CS registers
CSCTL0_H = 0x01; // Lock clock system registers
uint16_t length = 0xFFFF;
DMA_initParam cfg = {
DMA_CHANNEL_0, // channelSelect
DMA_TRANSFER_BLOCK, // transferModeSelect
length, // transferSize
DMA_TRIGGERSOURCE_0, // triggerSourceSelect
DMA_SIZE_SRCBYTE_DSTBYTE, // transferUnitSelect
DMA_TRIGGER_RISINGEDGE // triggerTypeSelect
};
uintptr_t src = (uintptr_t)(0x10000);
uintptr_t dst = (uintptr_t)(&CRC32DIRBB0);
DMA_init(&cfg);
HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) &= ~(DMASRCINCR | DMADSTINCR);
HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) |= DMASRCINCR_3;
__data20_write_long((uintptr_t)&DMA0SA, src);
__data20_write_long((uintptr_t)&DMA0DA, dst);
DMA0CTL &= ~DMAIFG;
DMA_enableTransfers(DMA_CHANNEL_0);
//DMA0CTL |= DMAREQ;
uint32_t timeout = 10000;
while (!(DMA0CTL & DMAIFG) && --timeout)
{
__no_operation();
}
/**/
while (1)
{
};
}When you step through is the device still hanging? Do you have a loop to wait for the crystal to start up?
Can you output the low frequency clock? Your DMAREQ line is after the clock check and I don't see why the compiler would be adjusting the DMA code to be before the clock check
Hi Luke,
let me unlock the thread again and post the latest feedback from our customer:
I just noticed somethin else: When using the DMA function in the lower addresses (16Bit) it works fine, but when using the higher FRAM area the device stops working.
I also found this note from the datasheet:

But let me show what I'm doing:
template <size_t VMaxChunkSize = 0xFFFF>
struct CRC32DataFeedMockWithDma
{
static constexpr auto maxChunkSize()
{
return VMaxChunkSize;
}
static struct Internals m_internals;
static void feed(const uint8_t *src, size_t length, const uint8_t *dst)
{
// Safety check: DMA transfer size is uint16_t, max 65535
if (length > 0xFFFF || length == 0)
{
return; // or assert/abort
}
DMA_initParam cfg = {
DMA_CHANNEL_0, // channelSelect
DMA_TRANSFER_BLOCK, // transferModeSelect
static_cast<uint16_t>(length), // transferSize
DMA_TRIGGERSOURCE_0, // triggerSourceSelect
DMA_SIZE_SRCBYTE_DSTBYTE, // transferUnitSelect
DMA_TRIGGER_RISINGEDGE // triggerTypeSelect
};
DMA_init(&cfg);
// Use proper 20-bit address handling
// Convert pointers to 32-bit first to avoid truncation
uint32_t volatile src_addr = reinterpret_cast<uint32_t>(src);
uint32_t volatile dst_addr = reinterpret_cast<uint32_t>(dst);
// Set the Source Address (20-bit)
__data20_write_long((uint32_t)&DMA0SA, src_addr & 0xFFFFF);
// Reset bits before setting them
HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) &= ~(DMASRCINCR_3);
HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) |= DMA_DIRECTION_INCREMENT;
// Set the Destination Address (20-bit)
__data20_write_long((uint32_t)&DMA0DA, dst_addr & 0xFFFFF);
HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) &= ~(DMADSTINCR_3);
HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) |= (DMA_DIRECTION_UNCHANGED << 2);
// Clear DMA interrupt flag and start transfer
DMA0CTL &= ~DMAIFG;
DMA_enableTransfers(DMA_CHANNEL_0);
DMA_startTransfer(DMA_CHANNEL_0);
// Poll for completion
while (!(DMA0CTL & DMAIFG))
{
__no_operation();
}
DMA_disableTransfers(DMA_CHANNEL_0);
}
};
Do you have any idea what could be wrong here?
Thank you!
**Attention** This is a public forum