Intel® Quark™ Microcontroller Software Interface  1.4.0
Intel® Quark™ Microcontroller BSP
newlib-syscalls.c
1 /*
2  * {% copyright %}
3  */
4 
5 /**
6  * newlib syscalls implementation
7  *
8  * @defgroup groupSyscalls Syscalls
9  * @{
10  */
11 
12 /*
13  * Implement newlib system calls and syscall-related functions.
14  *
15  * Implementation notes:
16  * - file descriptor support is limited to stdout/stderr, both targeting UART
17  */
18 
19 #include <sys/stat.h>
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 
24 #include "qm_common.h"
25 #include "qm_interrupt.h"
26 #include "qm_pinmux.h"
27 #include "clk.h"
28 #include "qm_uart.h"
29 
30 #define ASSERT_STR_HEAD ("\nAssertion failed: file ")
31 #define ASSERT_STR_TRAIL (", expr: ")
32 
33 #define PICO_PRINTF_D 1
34 #define PICO_PRINTF_U 1
35 #define PICO_PRINTF_X 1
36 #define PICO_PRINTF_S 1
37 #define PICO_PRINTF_ESC 1
38 #define FORMAT_BUF_SIZE 32
39 
40 static __inline__ int pico_putchar(int c)
41 {
42  qm_uart_write(STDOUT_UART, c);
43  return 1;
44 }
45 
46 #if (PICO_PRINTF_D || PICO_PRINTF_U || PICO_PRINTF_X)
47 static __inline__ char last_digit_to_char(unsigned int n, int base, bool upcase)
48 {
49  char c;
50 
51  c = n % base;
52  if (c <= 9)
53  return c + '0';
54 
55  return upcase ? c + ('A' - 10) : c + ('a' - 10);
56 }
57 
58 static __inline__ int putuint(unsigned int n, int base, bool upcase)
59 {
60  static char format_buf[FORMAT_BUF_SIZE];
61  char *s = format_buf;
62  int k;
63  int i = 0;
64 
65  do {
66  s[i++] = last_digit_to_char(n, base, upcase);
67  n /= base;
68  } while (n > 0);
69 
70  for (k = i - 1; k >= 0; k--)
71  pico_putchar(s[k]);
72 
73  return i;
74 }
75 #endif
76 
77 #if (PICO_PRINTF_S)
78 static __inline__ int pico_putchars(const char *s)
79 {
80  int len = 0;
81 
82  for (; *s; s++)
83  len += pico_putchar(*s);
84 
85  return len;
86 }
87 #endif
88 
89 /**
90  * @brief This is an minimally useful subset of the POSIX printf() function.
91  *
92  * To reduce code size, this pico_printf() implementation only supports a few
93  * conversion specifiers:
94  * - @a 'd', @a 'u': for signed and unsigned decimal numbers, respectively;
95  * - @a 'x', @a 'X': for hexadecimal numbers, downcase and upcase;
96  * - @a 's': for NULL terminated strings.
97  *
98  * Other limitations:
99  * - No flag specifier is implemented;
100  * - No field width specifier is implemented;
101  * - The only supported length modifier is 'l', which is parsed and ignored,
102  * on supported archictetures 'int' and 'long int' are both 32 bits long.
103  * - 32 digits maximum length for formatted numbers.
104  */
105 int pico_printf(const char *format, ...)
106 {
107  const char *s = format;
108  int len = 0;
109  va_list ap;
110  va_start(ap, format);
111 
112  while (*s) {
113  char c = *s++;
114  if (c == '%') {
115  c = *s++;
116  /*
117  * Ignore 'l' length sub-specifier.
118  * This has no effect in ILP32 (4/4/4).
119  * In i586, 'int' and 'long int' are both 4 bytes long.
120  */
121  if (c == 'l') {
122  c = *s++;
123  }
124  switch (c) {
125 #if (PICO_PRINTF_D)
126  case 'd': {
127  int n;
128  n = va_arg(ap, int);
129  if (n < 0) {
130  len += pico_putchar('-');
131  n = -n;
132  }
133  len += putuint(n, 10, false);
134  break;
135  }
136 #endif
137 #if (PICO_PRINTF_U)
138  case 'u': {
139  unsigned int u;
140  u = va_arg(ap, unsigned int);
141  len += putuint(u, 10, false);
142  break;
143  }
144 #endif
145 #if (PICO_PRINTF_X)
146  case 'X':
147  case 'x': {
148  unsigned int u;
149  u = va_arg(ap, unsigned int);
150  len += putuint(u, 16, c == 'X');
151  break;
152  }
153 #endif
154 #if (PICO_PRINTF_S)
155  case 's': {
156  const char *str;
157  str = va_arg(ap, const char *);
158  len += pico_putchars(str);
159  break;
160  }
161 #endif
162 #if (PICO_PRINTF_ESC)
163  default:
164  len += pico_putchar('%');
165  case '%':
166  len += pico_putchar(c);
167  break;
168 #endif
169  }
170  continue;
171  }
172 
173  len += pico_putchar(c);
174  }
175 
176  va_end(ap);
177  return len;
178 }
179 
180 static qm_uart_config_t stdout_uart_cfg;
181 #define QM_AON_GPIO ((qm_gpio_reg_t *)QM_AON_GPIO_BASE)
182 
183 void stdout_uart_setup(uint32_t baud_divisors)
184 {
185 /* Mux out TX pin */
186 
187 #if (QUARK_SE)
188 #if (STDOUT_UART_0)
190 #else
192 
193 #if (UART1_FTDI)
194  /* Set AON_GPIO 3 to get UART1 routed to USB/FTDI */
195  QM_AON_GPIO->gpio_swporta_dr |= BIT(3);
196 #else
197  QM_AON_GPIO->gpio_swporta_dr &= ~BIT(3);
198 #endif /* UART1_FTDI */
199  QM_AON_GPIO->gpio_swporta_ddr |= BIT(3);
200 #endif /* STDOUT_UART_0 */
201 #else
202 #if (STDOUT_UART_0)
204 #else
206 #endif /* STDOUT_UART_0 */
207 #endif /* QUARK_SE */
208 
209  stdout_uart_cfg.baud_divisor = baud_divisors;
210  stdout_uart_cfg.line_control = QM_UART_LC_8N1;
211 
212 #if (STDOUT_UART_0)
214 #else
216 #endif
217  qm_uart_set_config(STDOUT_UART, &stdout_uart_cfg);
218 }
219 
220 /*
221  * Custom puts implementation providing a lightweight alternative to the
222  * standard stream-compliant newlib implementation.
223  * Transmit a pre-formatted string through UART and append a newline to it.
224  */
225 int puts(const char *s)
226 {
227  for (; *s; s++) {
228  qm_uart_write(STDOUT_UART, *s);
229  }
230 
231  qm_uart_write(STDOUT_UART, '\n');
232  return 0;
233 }
234 
235 /*
236  * Custom lightweight implementation that avoids the need for
237  * kill/abort/exit/getpid syscalls. It is also optimised for size (no string
238  * formatting, targeting UART directly).
239  */
240 void __assert_func(const char *file, int line, const char *func,
241  const char *failedexpr)
242 {
243  (void)file;
244  (void)line;
245 
246  qm_irq_disable();
247 
248 #if (PUTS_ENABLE || PRINTF_ENABLE)
249  /* No point in checking for TX outcome at this stage */
250  qm_uart_write_buffer(STDOUT_UART, (uint8_t *)ASSERT_STR_HEAD,
251  sizeof(ASSERT_STR_HEAD));
252  int i;
253  for (i = 0; func[i]; i++) {
254  qm_uart_write(STDOUT_UART, (uint8_t)func[i]);
255  }
256  qm_uart_write_buffer(STDOUT_UART, (uint8_t *)ASSERT_STR_TRAIL,
257  sizeof(ASSERT_STR_TRAIL));
258  for (i = 0; failedexpr[i]; i++) {
259  qm_uart_write(STDOUT_UART, (uint8_t)failedexpr[i]);
260  }
261 #else
262  (void)func;
263  (void)failedexpr;
264 #endif /* PUTS_ENABLED || PRINTF_ENABLED */
265 
266  while (1) {
267 #if (ASSERT_ACTION_HALT)
268 #if (QM_SENSOR)
269  __builtin_arc_brk();
270 #else
271  __asm__ __volatile__("hlt");
272 #endif
273 #elif(ASSERT_ACTION_RESET)
275 #else
276 #error "Undefined assert action"
277 #endif /* ASSERT_ACTION_* */
278  }
279 }
280 
281 /* System call not supported */
282 int close(int file)
283 {
284  (void)file;
285 
286  errno = ENOTSUP;
287  return -1;
288 }
289 
290 /* System call not supported */
291 int isatty(int file)
292 {
293  (void)file;
294 
295  errno = ENOTTY;
296  return 0;
297 }
298 
299 /* System call not supported */
300 int read(int file, char *buf, int len)
301 {
302  (void)file;
303  (void)buf;
304  (void)len;
305 
306  errno = ENOTSUP;
307  return -1;
308 }
309 
310 /*
311  * Back-end of printf. Directs output and error streams to UART.
312  */
313 int write(int file, const char *buf, int len)
314 {
315  int ret = -1;
316 
317  switch (file) {
318  case 1:
319  case 2:
320  if (0 ==
321  qm_uart_write_buffer(STDOUT_UART, (uint8_t *)buf, len)) {
322  ret = len;
323  } else {
324  errno = EIO;
325  }
326  break;
327 
328  default:
329  errno = ENOTSUP;
330  break;
331  }
332 
333  return ret;
334 }
335 
336 /* System call not supported */
337 int lseek(int file, int p, int dir)
338 {
339  (void)file;
340  (void)p;
341  (void)dir;
342 
343  errno = ENOTSUP;
344  return -1;
345 }
346 
347 /* Returns file status. stdout/stderr set to character devices (needed by the
348  * printf family). */
349 int fstat(int file, struct stat *st)
350 {
351  if (file == 0 || file > 2) {
352  errno = ENOTSUP;
353  return -1;
354  }
355 
356  /* The stream is a character device (needed by printf functions) */
357  st->st_mode = S_IFCHR;
358  return 0;
359 }
360 
361 /*
362  * Expands/shrinks the heap. This is the back-end of malloc/free.
363  */
364 caddr_t sbrk(int incr)
365 {
366  extern char __heap, __heap_end;
367  static char *prog_break = &__heap;
368 
369  char *prev_prog_break;
370 
371  if (prog_break + incr >= &__heap_end || prog_break + incr < &__heap) {
372  errno = ENOMEM;
373  return (void *)-1;
374  }
375 
376  prev_prog_break = prog_break;
377 
378  prog_break += incr;
379 
380  return prev_prog_break;
381 }
382 
383 /**
384  * @}
385  */
UARTA Clock Gate Enable.
Definition: qm_soc_regs.h:1384
Pin id 20.
Definition: qm_pinmux.h:66
int clk_periph_enable(const clk_periph_t clocks)
Enable clocks for peripherals / registers.
Definition: clk.c:319
qm_uart_lc_t line_control
Line control (enum).
Definition: qm_uart.h:68
void qm_irq_disable(void)
Unconditionally disable interrupt delivery on the CPU.
Definition: qm_interrupt.c:31
Peripheral Clock Enable.
Definition: qm_soc_regs.h:1370
Warm reset.
Definition: qm_init.h:22
Gpio function 0.
Definition: qm_pinmux.h:22
int pico_printf(const char *format,...)
This is an minimally useful subset of the POSIX printf() function.
Pin id 12.
Definition: qm_pinmux.h:58
Pin id 16.
Definition: qm_pinmux.h:62
Gpio function 0.
Definition: qm_pinmux.h:24
int qm_pmux_select(const qm_pin_id_t pin, const qm_pmux_fn_t fn)
Set up pin muxing for a SoC pin.
Definition: qm_pinmux.c:37
int qm_uart_set_config(const qm_uart_t uart, const qm_uart_config_t *const cfg)
Set UART configuration.
Definition: qm_uart.c:215
UARTB Clock Gate Enable.
Definition: qm_soc_regs.h:1385
Pin id 19.
Definition: qm_pinmux.h:65
uint32_t baud_divisor
Baud Divisor.
Definition: qm_uart.h:69
void qm_soc_reset(qm_soc_reset_t reset_type)
Reset the SoC.
Definition: qm_init.c:7
int qm_uart_write(const qm_uart_t uart, const uint8_t data)
UART character data write.
Definition: qm_uart.c:280
UART configuration structure type.
Definition: qm_uart.h:67
int qm_uart_write_buffer(const qm_uart_t uart, const uint8_t *const data, const uint32_t len)
UART multi-byte data write.
Definition: qm_uart.c:342
8 data bits, no parity, 1 stop bit.
Definition: qm_uart.h:41