• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <atomic>
17 
18 #include "fsl_dma.h"
19 #include "fsl_inputmux.h"
20 #include "fsl_usart_dma.h"
21 #include "pw_bytes/byte_builder.h"
22 #include "pw_bytes/span.h"
23 #include "pw_clock_tree/clock_tree.h"
24 #include "pw_status/status.h"
25 #include "pw_stream/stream.h"
26 #include "pw_sync/interrupt_spin_lock.h"
27 #include "pw_sync/thread_notification.h"
28 
29 namespace pw::stream {
30 
31 class UartDmaStreamMcuxpresso final : public NonSeekableReaderWriter {
32  public:
33   // Configuration structure
34   struct Config {
35     USART_Type* usart_base;            // Base of USART control struct
36     uint32_t baud_rate;                // Desired communication speed
37     usart_parity_mode_t parity;        // Parity setting
38     usart_stop_bit_count_t stop_bits;  // Number of stop bits to use
39     DMA_Type* dma_base;                // Base of DMA control struct
40     uint32_t rx_dma_ch;                // Receive DMA channel
41     uint32_t tx_dma_ch;                // Transmit DMA channel
42     inputmux_signal_t rx_input_mux_dmac_ch_request_en;  // Rx input mux signal
43     inputmux_signal_t tx_input_mux_dmac_ch_request_en;  // Tx input mux signal
44     ByteSpan buffer;                                    // Receive ring buffer
45     pw::clock_tree::ClockTree* clock_tree{};            // Optional clock Tree
46     pw::clock_tree::Element*
47         clock_tree_element{};  // Optional clock tree element
48   };
49 
UartDmaStreamMcuxpresso(const Config & config)50   UartDmaStreamMcuxpresso(const Config& config)
51       : rx_data_{.ring_buffer = config.buffer},
52         config_(config),
53         clock_tree_element_controller_(config.clock_tree,
54                                        config.clock_tree_element) {}
55 
56   ~UartDmaStreamMcuxpresso();
57 
58   UartDmaStreamMcuxpresso(const UartDmaStreamMcuxpresso& other) = delete;
59 
60   UartDmaStreamMcuxpresso& operator=(const UartDmaStreamMcuxpresso& other) =
61       delete;
62 
63   pw::Status Init(uint32_t srcclk);
64 
65  private:
66   // Usart DMA TX data structure
67   struct UsartDmaTxData {
68     ConstByteSpan buffer;       // TX transaction buffer
69     size_t tx_idx;              // Position within TX transaction
70     dma_handle_t dma_handle;    // DMA handle
71     usart_transfer_t transfer;  // USART TX transfer structure
72     std::atomic_uint8_t busy;   // Flag to prevent concurrent access to TX queue
73     pw::sync::ThreadNotification notification;  // TX completion notification
74   };
75 
76   // Usart DMA RX data structure
77   struct UsartDmaRxData {
78     ByteSpan ring_buffer;            // Receive ring buffer
79     size_t ring_buffer_read_idx{};   // ring buffer reader index
80     size_t ring_buffer_write_idx{};  // ring buffer writer index
81     size_t data_received{};  // data received and acknowledged by completion
82                              // callback
83     size_t data_copied{};    // data copied out to receiver
84     // completion callback will be executed when completion size decreases to 0
85     // bytes
86     size_t completion_size{};
87     usart_transfer_t transfer{};  // USART RX transfer structure
88     dma_handle_t dma_handle{};    // DMA handle
89     std::atomic_uint8_t
90         busy{};  // Flag to prevent concurrent access to RX ring buffer
91     pw::sync::ThreadNotification notification{};  // RX completion notification
92   };
93 
94   // Since we are calling USART_TransferGetReceiveCountDMA we may only
95   // transfer DMA_MAX_TRANSFER_COUNT - 1 bytes per DMA transfer.
96   static constexpr size_t kUsartDmaMaxTransferCount =
97       DMA_MAX_TRANSFER_COUNT - 1;
98 
99   // A reader may at most wait for 25% of the ring buffer size before data
100   // needs to be copied out to the caller.
101   static constexpr size_t kUsartRxRingBufferSplitCount = 4;
102 
103   StatusWithSize DoRead(ByteSpan) override;
104   Status DoWrite(ConstByteSpan) override;
105 
106   // Helper functions
107   static IRQn_Type GetInterrupt(const DMA_Type* base);
108   void Deinit();
109   void TriggerReadDma() PW_EXCLUSIVE_LOCKS_REQUIRED(interrupt_lock_);
110   void TriggerWriteDma();
111 
112   StatusWithSize TransferGetReceiveDMACount()
113       PW_LOCKS_EXCLUDED(interrupt_lock_);
114   StatusWithSize TransferGetReceiveDMACountLockHeld()
115       PW_EXCLUSIVE_LOCKS_REQUIRED(interrupt_lock_);
116   size_t GetReceiveTransferRemainingBytes();
117   static void TxRxCompletionCallback(USART_Type* base,
118                                      usart_dma_handle_t* state,
119                                      status_t status,
120                                      void* param);
121   Status WaitForReceiveBytes(size_t bytes_needed);
122   void CopyReceiveData(ByteBuilder& bb, size_t copy_size);
123 
124   pw::sync::InterruptSpinLock
125       interrupt_lock_;  // Lock to synchronize with interrupt handler and to
126                         // guarantee exclusive access to DMA control registers
127   usart_dma_handle_t uart_dma_handle_;  // USART DMA Handle
128   struct UsartDmaTxData tx_data_;       // TX data
129   struct UsartDmaRxData rx_data_;       // RX data
130 
131   Config const config_;  // USART DMA configuration
132   pw::clock_tree::ElementController
133       clock_tree_element_controller_;  // Element controller encapsulating
134                                        // optional clock tree information
135   bool
136       initialized_;  // Whether the USART and DMA channels have been initialized
137 };
138 
139 }  // namespace pw::stream
140