2024-07-21 12:45 AM - edited 2024-07-21 12:51 AM
I'm trying to use DMA with the USART2 receiver in half-duplex mode but the DMA indicates a device error as soon as it begins even though no error flag is set in the ISR register either before the DMA starts or after the DMA ends, additionally the same code works fine when reading the RDR register in a loop. After the DMA fails the data that it should have received can still be read from the USART. I'm also using the same DMA for USART3 in an identical way aside from it not being used in half-duplex mode. So does anyone know what could be causing this?
Here is my code and the output:
TMC_UART_Internal.CR1.TE := False;
for Byte of Input loop
Transmit (TMC_UART, UInt9 (Byte));
Server_Communication.Transmit_String_Line ("In TX FIFO: " & Byte'Image);
end loop;
Server_Communication.Transmit_String ("ISR: ");
Server_Communication.Transmit_String_Line (TMC_UART_Internal.ISR'Image);
Start_Transfer
(This => TMC_UART_DMA_RX_Controller,
Stream => TMC_UART_DMA_RX_Stream,
Source => Read_Data_Register_Address (TMC_UART),
Destination => RX_Buffer'Address,
Data_Count => RX_Buffer'Length);
TMC_UART_Internal.CR1.TE := True;
declare
Error : DMA_Error_Code;
begin
Poll_For_Completion
(This => TMC_UART_DMA_RX_Controller,
Stream => TMC_UART_DMA_RX_Stream,
Expected_Level => Full_Transfer,
Timeout => Milliseconds (100),
Result => Error);
if Error /= DMA_No_Error then
Server_Communication.Transmit_String_Line ("RX data:");
while Rx_Ready (TMC_UART) or TMC_UART_Internal.ISR.BUSY loop
Server_Communication.Transmit_String_Line (TMC_UART_Internal.RDR.RDR'Image);
end loop;
Server_Communication.Transmit_String ("TMC DMA error: ");
Server_Communication.Transmit_String_Line (Error'Image);
Server_Communication.Transmit_String_Line ("DMA received data: ");
Server_Communication.Transmit_String_Line (RX_Buffer'Image);;
Server_Communication.Transmit_String ("ISR: ");
Server_Communication.Transmit_String_Line (TMC_UART_Internal.ISR'Image);
while Rx_Ready (TMC_UART) or TMC_UART_Internal.ISR.BUSY loop
Server_Communication.Transmit_String_Line (TMC_UART_Internal.RDR.RDR'Image);
end loop;
Receive_Failed := True;
else
Server_Communication.Transmit_String ("TMC DMA good: ");
Server_Communication.Transmit_String_Line (Error'Image);
Server_Communication.Transmit_String_Line (Input'Image);
Server_Communication.Transmit_String_Line (RX_Buffer'Image);
end if;
end;
In TX FIFO: 5
In TX FIFO: 1
In TX FIFO: 4
In TX FIFO: 30
ISR:
(PE => FALSE,
FE => FALSE,
NF => FALSE,
ORE => FALSE,
IDLE => FALSE,
RXNE => FALSE,
TC => FALSE,
TXE => TRUE,
LBDF => FALSE,
CTSIF => FALSE,
CTS => FALSE,
RTOF => FALSE,
EOBF => FALSE,
UDR => FALSE,
ABRE => FALSE,
ABRF => FALSE,
BUSY => FALSE,
CMF => FALSE,
SBKF => FALSE,
RWU => FALSE,
WUF => FALSE,
TEACK => FALSE,
REACK => TRUE,
TXFE => FALSE,
RXFF => FALSE,
TCBGT => FALSE,
RXFT => FALSE,
TXFT => TRUE,
RESERVED_28_31 => 0)
RX data: (Note that busy has not even been set at this point, but the DMA has already indicated an error)
TMC DMA error: DMA_DEVICE_ERROR
DMA received data: (Note that 123 was put there by me to make sure that DMA was not writing to the buffer)
[ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
123, 123]
ISR:
(PE => FALSE,
FE => FALSE,
NF => FALSE,
ORE => FALSE,
IDLE => FALSE,
RXNE => FALSE,
TC => FALSE,
TXE => TRUE,
LBDF => FALSE,
CTSIF => FALSE,
CTS => FALSE,
RTOF => FALSE,
EOBF => FALSE,
UDR => FALSE,
ABRE => FALSE,
ABRF => FALSE,
BUSY => TRUE,
CMF => FALSE,
SBKF => FALSE,
RWU => FALSE,
WUF => FALSE,
TEACK => TRUE,
REACK => TRUE,
TXFE => FALSE,
RXFF => FALSE,
TCBGT => FALSE,
RXFT => TRUE,
TXFT => TRUE,
RESERVED_28_31 => 0)
5
1
4
30
30
30
30
30
30
Solved! Go to Solution.
2024-07-21 03:51 AM - edited 2024-07-21 03:55 AM
While it's probably not documented explicitly, the CCM SRAM cannot be accessed at the 0x1000'xxxx address from the DMA; those addresses are in the bus matrix mapped only on the D-port of the processor.
Instead, use the aliased CCM SRAM address (which depends on which particular STM32G4xx you are using).
> I thought that ISR.TEIFx was set when the peripheral that was being read from indicated an error, however this appears to not be the case, instead it is set when reading from or writing to a reserved address space.
Indeed, as documented. Error signals are not propagated from peripherals to DMA. In fact, the interconnections between modules within the SoC are as limited as possible.
JW
2024-07-21 01:00 AM
> DMA indicates a device error as soon as it begins even though no error flag is set in the ISR register
So, how would DMA indicate an error in any other way than setting a flag in the DMA_ISR register?
What is this, Rust?
In any case, obviously, there are some "drivers" under the hood, which read and the clear the flags in the ISR register; you then just are notified in some way by those "drivers".
Maybe somebody can guess from that description what your problem is, or is experienced with the environment you are using enough to tell you the reason for your problem. If nobody, I'd suggest you to go programming directly at the register level (which is the common denominator we all understand and can check against the RM), and then if you still have problems, come back with the findings.
JW
2024-07-21 01:21 AM
This is Ada and nothing is touching the ISR register in between these two calls. Poll_For_Completion only looks at the ISR on the DMA, and the error indicates that ISR.TEIF1 is set. Here's a dump of the relevant USART2 and DMA registers. I can convert all the code to direct register writes if something here does not indicate what's going wrong:
USART2:
CR1:
(UE => TRUE,
UESM => FALSE,
RE => TRUE,
TE => TRUE,
IDLEIE => FALSE,
RXNEIE => FALSE,
TCIE => FALSE,
TXEIE => FALSE,
PEIE => FALSE,
PS => FALSE,
PCE => FALSE,
WAKE => FALSE,
M0 => FALSE,
MME => FALSE,
CMIE => FALSE,
OVER8 => FALSE,
DEDT => {STM32_SVD.USART.CR1_DEDT_FIELD object},
DEAT => {STM32_SVD.USART.CR1_DEAT_FIELD object},
RTOIE => FALSE,
EOBIE => FALSE,
M1 => FALSE,
FIFOEN => TRUE,
TXFEIE => FALSE,
RXFFIE => FALSE)
CR2:
(SLVEN => FALSE,
RESERVED_1_2 => 0,
DIS_NSS => FALSE,
ADDM7 => FALSE,
LBDL => FALSE,
LBDIE => FALSE,
RESERVED_7_7 => 0,
LBCL => FALSE,
CPHA => FALSE,
CPOL => FALSE,
CLKEN => FALSE,
STOP => 0,
LINEN => FALSE,
SWAP => FALSE,
RXINV => FALSE,
TXINV => FALSE,
TAINV => FALSE,
MSBFIRST => FALSE,
ABREN => FALSE,
ABRMOD => {STM32_SVD.USART.CR2_ABRMOD_FIELD object},
RTOEN => FALSE,
ADD => {STM32_SVD.USART.CR2_ADD_FIELD object})
CR3:
(EIE => FALSE,
IREN => FALSE,
IRLP => FALSE,
HDSEL => TRUE,
NACK => FALSE,
SCEN => FALSE,
DMAR => TRUE,
DMAT => FALSE,
RTSE => FALSE,
CTSE => FALSE,
CTSIE => FALSE,
ONEBIT => FALSE,
OVRDIS => FALSE,
DDRE => FALSE,
DEM => FALSE,
DEP => FALSE,
RESERVED_16_16 => 0,
SCARCNT => 0,
WUS => 0,
WUFIE => FALSE,
TXFTIE => FALSE,
TCBGTIE => FALSE,
RXFTCFG => 0,
RXFTIE => FALSE,
TXFTCFG => 0)
BRR:
(DIV_FRACTION => 4,
DIV_MANTISSA => 488,
RESERVED_16_31 => 0)
GTPR:
(PSC => 0,
GT => 0,
RESERVED_16_31 => 0)
RTOR:
(RTO => 0,
BLEN => 0)
RQR:
(ABRRQ => FALSE,
SBKRQ => FALSE,
MMRQ => FALSE,
RXFRQ => FALSE,
TXFRQ => FALSE,
RESERVED_5_31 => 0)
ISR:
(PE => FALSE,
FE => FALSE,
NF => FALSE,
ORE => TRUE,
IDLE => TRUE,
RXNE => TRUE,
TC => TRUE,
TXE => TRUE,
LBDF => FALSE,
CTSIF => FALSE,
CTS => FALSE,
RTOF => FALSE,
EOBF => TRUE,
UDR => FALSE,
ABRE => FALSE,
ABRF => FALSE,
BUSY => FALSE,
CMF => FALSE,
SBKF => FALSE,
RWU => FALSE,
WUF => FALSE,
TEACK => TRUE,
REACK => TRUE,
TXFE => TRUE,
RXFF => TRUE,
TCBGT => FALSE,
RXFT => TRUE,
TXFT => TRUE,
RESERVED_28_31 => 0)
PRESC:
(PRESCALER => 0,
RESERVED_4_31 => 0)
DMA:
ISR before Start_Transfer:
(GIF1 => FALSE,
TCIF1 => FALSE,
HTIF1 => FALSE,
TEIF1 => FALSE,
GIF2 => FALSE,
TCIF2 => FALSE,
HTIF2 => FALSE,
TEIF2 => FALSE,
GIF3 => FALSE,
TCIF3 => FALSE,
HTIF3 => FALSE,
TEIF3 => FALSE,
GIF4 => FALSE,
TCIF4 => FALSE,
HTIF4 => FALSE,
TEIF4 => FALSE,
GIF5 => FALSE,
TCIF5 => FALSE,
HTIF5 => FALSE,
TEIF5 => FALSE,
GIF6 => FALSE,
TCIF6 => FALSE,
HTIF6 => FALSE,
TEIF6 => FALSE,
GIF7 => FALSE,
TCIF7 => FALSE,
HTIF7 => FALSE,
TEIF7 => FALSE,
GIF8 => FALSE,
TCIF8 => FALSE,
HTIF8 => FALSE,
TEIF8 => FALSE)
ISR after Start_Transfer:
(GIF1 => FALSE,
TCIF1 => FALSE,
HTIF1 => FALSE,
TEIF1 => FALSE,
GIF2 => TRUE,
TCIF2 => FALSE,
HTIF2 => FALSE,
TEIF2 => TRUE,
GIF3 => FALSE,
TCIF3 => FALSE,
HTIF3 => FALSE,
TEIF3 => FALSE,
GIF4 => FALSE,
TCIF4 => FALSE,
HTIF4 => FALSE,
TEIF4 => FALSE,
GIF5 => FALSE,
TCIF5 => FALSE,
HTIF5 => FALSE,
TEIF5 => FALSE,
GIF6 => FALSE,
TCIF6 => FALSE,
HTIF6 => FALSE,
TEIF6 => FALSE,
GIF7 => FALSE,
TCIF7 => FALSE,
HTIF7 => FALSE,
TEIF7 => FALSE,
GIF8 => FALSE,
TCIF8 => FALSE,
HTIF8 => FALSE,
TEIF8 => FALSE)
CCR2:
(EN => FALSE,
TCIE => FALSE,
HTIE => FALSE,
TEIE => FALSE,
DIR => FALSE,
CIRC => FALSE,
PINC => FALSE,
MINC => TRUE,
PSIZE => 0,
MSIZE => 0,
PL => 0,
MEM2MEM => FALSE,
RESERVED_15_31 => 0)
CNDTR2:
(NDT => 12,
RESERVED_16_31 => 0)
CPAR2: 1073759268
CMAR2: 268456164
2024-07-21 01:31 AM
I forgot DMAMUX:
DMAMUX.C1CR:
(DMAREQ_ID => 26,
RESERVED_7_7 => 0,
SOIE => FALSE,
EGE => FALSE,
RESERVED_10_15 => 0,
SE => FALSE,
SPOL => 0,
NBREQ => 0,
SYNC_ID => 0,
RESERVED_29_31 => 0)
2024-07-21 01:36 AM
> So, how would DMA indicate an error in any other way than setting a flag in the DMA_ISR register?
To be clear I mean there is no error in the USART ISR register. The DMA ISR register error flags have no reason to be set in this case.
2024-07-21 03:19 AM
> The DMA ISR register error flags have no reason to be set in this case.
How did you come to this conclusion? Have you actually checked the DMA_ISR register? Have you single-stepped the "driver" you are using to understand the root of the problem?
Or is the error message returned by the "driver" descriptive enough to come to this conclusion?\
Apparently not.
What do you expect from us, then?
JW
2024-07-21 03:42 AM
> How did you come to this conclusion?
I thought that ISR.TEIFx was set when the peripheral that was being read from indicated an error, however this appears to not be the case, instead it is set when reading from or writing to a reserved address space. This however makes things even more confusing as the two addresses in the DMA registers shown above (0x40004424 and 0x100050E4) are most certainly not in reserved address spaces, the first is the USART2 RDR register and the second is in CCM SRAM.
> Have you actually checked the DMA_ISR register?
Yes, that is what is shown above, TEIF2 => FALSE before enabling the DMA and TEIF2 => TRUE after.
> Have you single-stepped the "driver" you are using to understand the root of the problem?
I unfortunately do not have exposed debug pins here, I have however checked the registers being set by the Start_Transfer procedure and they are all correct:
CR.EN := False;
loop until the bit reads as False
NDTR.NDT := 12;
PAR := 0x40004424;
MAR := 0x100050E4;
CR.EN := True;
All the other registers that are set earlier are shown above.
2024-07-21 03:51 AM - edited 2024-07-21 03:55 AM
While it's probably not documented explicitly, the CCM SRAM cannot be accessed at the 0x1000'xxxx address from the DMA; those addresses are in the bus matrix mapped only on the D-port of the processor.
Instead, use the aliased CCM SRAM address (which depends on which particular STM32G4xx you are using).
> I thought that ISR.TEIFx was set when the peripheral that was being read from indicated an error, however this appears to not be the case, instead it is set when reading from or writing to a reserved address space.
Indeed, as documented. Error signals are not propagated from peripherals to DMA. In fact, the interconnections between modules within the SoC are as limited as possible.
JW
2024-07-21 04:10 AM
That's an annoying thing for them to not document, especially when the RM explicitly says that the error only occurs when an address is in the reserved space, not an inaccessible-to-DMA space. I've just moved the buffer out of CCM, which solves the problem.