xref: /aosp_15_r20/external/pigweed/pw_sys_io_baremetal_lm3s6965evb/sys_io_baremetal.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 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 
15 #include <cinttypes>
16 
17 #include "pw_preprocessor/compiler.h"
18 #include "pw_sys_io/sys_io.h"
19 
20 namespace {
21 
22 // Default core clock. This is technically not a constant, but since this app
23 // doesn't change the system clock a constant will suffice.
24 constexpr uint32_t kSystemCoreClock = 12000000;
25 
26 // UART status flags.
27 constexpr uint32_t kTxFifoEmptyMask = 0b10000000;
28 constexpr uint32_t kRxFifoFullMask = 0b100000;
29 
30 // UART line control flags.
31 // Default: 8n1
32 constexpr uint32_t kDefaultLineControl = 0x60;
33 
34 // UART control flags.
35 constexpr uint32_t kUartEnableMask = 0x1;
36 
PW_PACKED(struct)37 PW_PACKED(struct) UartBlock {
38   uint32_t data_register;
39   uint32_t receive_error;
40   uint32_t reserved1[4];
41   uint32_t status_flags;
42   uint32_t reserved2;
43   uint32_t low_power;
44   uint32_t integer_baud;
45   uint32_t fractional_baud;
46   uint32_t line_control;
47   uint32_t control;
48   uint32_t interrupt_fifo_level;
49   uint32_t interrupt_mask;
50   uint32_t raw_interrupt;
51   uint32_t masked_interrupt;
52   uint32_t interrupt_clear;
53 };
54 
55 // Declare a reference to the memory mapped block for UART0.
56 volatile UartBlock& uart0 = *reinterpret_cast<volatile UartBlock*>(0x4000C000U);
57 
58 constexpr uint32_t kRcgcUart0EnableMask = 0x1;
59 volatile uint32_t& rcgc1 = *reinterpret_cast<volatile uint32_t*>(0x400FE104U);
60 
61 // Calculate a baud rate multiplier such that we have 16 bits of precision for
62 // the integer portion and 6 bits for the fractional portion.
SetBaudRate(uint32_t clock,uint32_t target_baud)63 void SetBaudRate(uint32_t clock, uint32_t target_baud) {
64   uint32_t divisor = target_baud * 16;
65   uint32_t remainder = clock % divisor;
66   uart0.integer_baud = (clock % divisor) & 0xffff;
67   uart0.fractional_baud = (((remainder << 7) / divisor + 1) >> 1) & 0x3f;
68 }
69 
70 }  // namespace
71 
pw_sys_io_lm3s6965evb_Init()72 extern "C" void pw_sys_io_lm3s6965evb_Init() {
73   rcgc1 |= kRcgcUart0EnableMask;
74 
75   for (volatile int i = 0; i < 3; i = i + 1) {
76     // We must wait after enabling uart.
77   }
78   // Set baud rate.
79   SetBaudRate(kSystemCoreClock, /*target_baud=*/115200);
80   uart0.line_control = kDefaultLineControl;
81   uart0.control |= kUartEnableMask;
82 }
83 
84 namespace pw::sys_io {
85 
86 // Wait for a byte to read on UART0. This blocks until a byte is read. This is
87 // extremely inefficient as it requires the target to burn CPU cycles polling to
88 // see if a byte is ready yet.
ReadByte(std::byte * dest)89 Status ReadByte(std::byte* dest) {
90   while (true) {
91     if (TryReadByte(dest).ok()) {
92       return OkStatus();
93     }
94   }
95 }
96 
TryReadByte(std::byte * dest)97 Status TryReadByte(std::byte* dest) {
98   if (uart0.receive_error) {
99     // Writing anything to this register clears all errors.
100     uart0.receive_error = 0xff;
101   }
102   if (!(uart0.status_flags & kRxFifoFullMask)) {
103     return Status::Unavailable();
104   }
105   *dest = static_cast<std::byte>(uart0.data_register);
106   return OkStatus();
107 }
108 
109 // Send a byte over UART0. Since this blocks on every byte, it's rather
110 // inefficient. At the default baud rate of 115200, one byte blocks the CPU for
111 // ~87 micro seconds. This means it takes only 10 bytes to block the CPU for
112 // 1ms!
WriteByte(std::byte b)113 Status WriteByte(std::byte b) {
114   // Wait for TX buffer to be empty. When the buffer is empty, we can write
115   // a value to be dumped out of UART.
116   while (!(uart0.status_flags & kTxFifoEmptyMask)) {
117   }
118   uart0.data_register = static_cast<uint32_t>(b);
119   return OkStatus();
120 }
121 
122 // Writes a string using pw::sys_io, and add newline characters at the end.
WriteLine(std::string_view s)123 StatusWithSize WriteLine(std::string_view s) {
124   size_t chars_written = 0;
125   StatusWithSize result = WriteBytes(as_bytes(span(s)));
126   if (!result.ok()) {
127     return result;
128   }
129   chars_written += result.size();
130 
131   // Write trailing newline.
132   result = WriteBytes(as_bytes(span("\r\n", 2)));
133   chars_written += result.size();
134 
135   return StatusWithSize(result.status(), chars_written);
136 }
137 
138 }  // namespace pw::sys_io
139