Skyward boardcore
Loading...
Searching...
No Matches
USART.cpp
Go to the documentation of this file.
1/* Copyright (c) 2022 Skyward Experimental Rocketry
2 * Author: Emilio Corigliano
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "drivers/usart/USART.h"
24
25#include <fcntl.h>
26#include <stdio.h>
27#include <utils/Debug.h>
28#include <utils/Numeric.h>
29
30#include <fstream>
31#include <string>
32
33#include "arch/common/drivers/serial.h"
34#include "filesystem/file_access.h"
35#include "kernel/scheduler/scheduler.h"
36#include "miosix.h"
37
39Boardcore::USART* ports[N_USART_PORTS];
40
41#ifdef USART1
45void usart1irqImpl();
46
50void __attribute__((used)) usart1irqImplBoardcore()
51{
52 Boardcore::USART* port_boardcore = ports[0];
53 if (port_boardcore)
54 port_boardcore->IRQhandleInterrupt();
55 else
56 usart1irqImpl();
57}
58
62void __attribute__((naked, used)) USART1_IRQHandler()
63{
64 saveContext();
65 asm volatile("bl _Z22usart1irqImplBoardcorev");
66 restoreContext();
67}
68#endif
69
70#ifdef USART2
74void usart2irqImpl();
75
79void __attribute__((used)) usart2irqImplBoardcore()
80{
81 Boardcore::USART* port_boardcore = ports[1];
82 if (port_boardcore)
83 port_boardcore->IRQhandleInterrupt();
84 else
85 usart2irqImpl();
86}
87
91void __attribute__((naked, used)) USART2_IRQHandler()
92{
93 saveContext();
94 asm volatile("bl _Z22usart2irqImplBoardcorev");
95 restoreContext();
96}
97#endif
98
99#ifdef USART3
103void usart3irqImpl();
104
108void __attribute__((used)) usart3irqImplBoardcore()
109{
110 Boardcore::USART* port_boardcore = ports[2];
111 if (port_boardcore)
112 port_boardcore->IRQhandleInterrupt();
113 else
114 usart3irqImpl();
115}
116
120void __attribute__((naked, used)) USART3_IRQHandler()
121{
122 saveContext();
123 asm volatile("bl _Z22usart3irqImplBoardcorev");
124 restoreContext();
125}
126#endif
127
128#ifdef UART4
132void __attribute__((used)) uart4irqImplBoardcore()
133{
134 Boardcore::USART* port_boardcore = ports[3];
135 if (port_boardcore)
136 port_boardcore->IRQhandleInterrupt();
137}
138
142void __attribute__((naked, used)) UART4_IRQHandler()
143{
144 saveContext();
145 asm volatile("bl _Z21uart4irqImplBoardcorev");
146 restoreContext();
147}
148#endif
149
150#ifdef UART5
154void __attribute__((used)) uart5irqImplBoardcore()
155{
156 Boardcore::USART* port_boardcore = ports[4];
157 if (port_boardcore)
158 port_boardcore->IRQhandleInterrupt();
159}
160
164void __attribute__((naked, used)) UART5_IRQHandler()
165{
166 saveContext();
167 asm volatile("bl _Z21uart5irqImplBoardcorev");
168 restoreContext();
169}
170#endif
171
172#ifdef USART6
176void __attribute__((used)) usart6irqImplBoardcore()
177{
178 Boardcore::USART* port_boardcore = ports[5];
179 if (port_boardcore)
180 port_boardcore->IRQhandleInterrupt();
181}
182
186void __attribute__((naked, used)) USART6_IRQHandler()
187{
188 saveContext();
189 asm volatile("bl _Z22usart6irqImplBoardcorev");
190 restoreContext();
191}
192#endif
193
194#ifdef UART7
198void __attribute__((used)) uart7irqImplBoardcore()
199{
200 Boardcore::USART* port_boardcore = ports[6];
201 if (port_boardcore)
202 port_boardcore->IRQhandleInterrupt();
203}
204
208void __attribute__((naked, used)) UART7_IRQHandler()
209{
210 saveContext();
211 asm volatile("bl _Z21uart7irqImplBoardcorev");
212 restoreContext();
213}
214#endif
215
216#ifdef UART8
220void __attribute__((used)) uart8irqImplBoardcore()
221{
222 Boardcore::USART* port_boardcore = ports[7];
223 if (port_boardcore)
224 port_boardcore->IRQhandleInterrupt();
225}
226
230void __attribute__((naked, used)) UART8_IRQHandler()
231{
232 saveContext();
233 asm volatile("bl _Z21uart8irqImplBoardcorev");
234 restoreContext();
235}
236#endif
237
238namespace Boardcore
239{
240
242 : usart(usart), baudrate(baudrate)
243{
244 // Setting the id of the serial port
245 switch (reinterpret_cast<uint32_t>(usart))
246 {
247#ifdef USART1
248 case USART1_BASE:
249 this->id = 1;
250 irqn = USART1_IRQn;
251 this->serialPortName = std::string("usart1");
252 break;
253#endif
254#ifdef USART2
255 case USART2_BASE:
256 this->id = 2;
257 irqn = USART2_IRQn;
258 this->serialPortName = std::string("usart2");
259 break;
260#endif
261#ifdef USART3
262 case USART3_BASE:
263 this->id = 3;
264 irqn = USART3_IRQn;
265 this->serialPortName = std::string("usart3");
266 break;
267#endif
268#ifdef UART4
269 case UART4_BASE:
270 this->id = 4;
271 irqn = UART4_IRQn;
272 this->serialPortName = std::string("uart4");
273 break;
274#endif
275#ifdef UART5
276 case UART5_BASE:
277 this->id = 5;
278 irqn = UART5_IRQn;
279 this->serialPortName = std::string("uart5");
280 break;
281#endif
282#ifdef USART6
283 case USART6_BASE:
284 this->id = 6;
285 irqn = USART6_IRQn;
286 this->serialPortName = std::string("usart6");
287 break;
288#endif
289#ifdef UART7
290 case UART7_BASE:
291 this->id = 7;
292 irqn = UART7_IRQn;
293 this->serialPortName = std::string("uart7");
294 break;
295#endif
296#ifdef UART8
297 case UART8_BASE:
298 this->id = 8;
299 irqn = UART8_IRQn;
300 this->serialPortName = std::string("uart8");
301 break;
302#endif
303 default:
304 LOG_ERR(logger, "USART selected not supported!");
305 D(assert(false && "USART selected not supported!"));
306 }
307}
308
310
312{
313 char c;
314 bool received = false;
315 bool framingError;
316
317#ifndef _ARCH_CORTEXM7_STM32F7
318 // If read data register is empty then read data
319 received = ((usart->SR & USART_SR_RXNE) == 0 ? false : true);
320 // If no error put data in buffer
321 framingError = ((usart->SR & USART_SR_FE) == 0 ? false : true);
322 idle = ((usart->SR & USART_SR_IDLE) == 0 ? false : true);
323 // Always read data, since this clears interrupt flags
324 c = usart->DR;
325#else
326 // If read data register is empty then read data
327 received = ((usart->ISR & USART_ISR_RXNE) == 0 ? false : true);
328 // If no error put data in buffer
329 framingError = ((usart->ISR & USART_ISR_FE) == 0 ? false : true);
330 idle = ((usart->ISR & USART_ISR_IDLE) == 0 ? false : true);
331 // Clears interrupt flags
332 usart->ICR = USART_ICR_IDLECF;
333 // Always read data, since this clears interrupt flags
334 c = usart->RDR;
335#endif
336
337 // If we received some data without framing error but the tryPut failed,
338 // report a FIFO overflow
339 if (framingError || (received && !rxQueue.tryPut(c)))
340 error = true;
341
342 // Wake up thread if communication finished (idle state), buffer reached
343 // half of his capacity or error occurred
344 if (error || idle || (rxQueue.size() >= rxQueue.capacity() / 2))
345 {
346 // Enough data in buffer or idle line, awake thread
347 if (rxWaiter)
348 {
349 rxWaiter->IRQwakeup();
350 if (rxWaiter->IRQgetPriority() >
351 miosix::Thread::IRQgetCurrentThread()->IRQgetPriority())
352 {
353 miosix::Scheduler::IRQfindNextThread();
354 }
355 rxWaiter = nullptr;
356 }
357 }
358}
359
360USART::USART(USARTType* usart, int baudrate, unsigned int queueLen)
361 : USARTInterface(usart, baudrate), rxQueue(queueLen)
362{
363 // Enabling the peripheral on the right APB
365
366 // Setting the baudrate chosen
368
369 // Default settings
370 setStopBits(1);
373 setOversampling(false);
374
375 {
376 miosix::FastInterruptDisableLock dLock;
377
378 // Enable usart, receiver, receiver interrupt and idle interrupt
379 usart->CR1 |= USART_CR1_UE // Enabling the uart peripheral
380 | USART_CR1_RXNEIE // Interrupt on data received
381 | USART_CR1_IDLEIE // interrupt on idle line
382 | USART_CR1_TE // Transmission enabled
383 | USART_CR1_RE; // Reception enabled
384
385 // Sample only one bit
386 usart->CR3 |= USART_CR3_ONEBIT;
387 }
388
389 // Add to the array of usarts so that the interrupts can see it
390 ports[id - 1] = this;
391
392 // Enabling the interrupt for the relative serial port
393 NVIC_SetPriority(irqn, 15);
394 NVIC_EnableIRQ(irqn);
395
396 // Clearing the queue for random data read at the beginning
397 this->clearQueue();
398}
399
401{
402 miosix::FastInterruptDisableLock dLock;
403
404 // Take out the usart object we are going to destruct
405 ports[this->id - 1] = nullptr;
406
407 // Disabling the usart
408 usart->CR1 &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
409
410 // Disabling the interrupt of the serial port
411 NVIC_DisableIRQ(irqn);
412}
413
415{
416 miosix::FastInterruptDisableLock dLock;
417 (wordLength == WordLength::BIT8 ? usart->CR1 &= ~USART_CR1_M
418 : usart->CR1 |= USART_CR1_M);
419 this->wordLength = wordLength;
420}
421
423{
424 miosix::FastInterruptDisableLock dLock;
425 (parity == ParityBit::NO_PARITY ? usart->CR1 &= ~USART_CR1_PCE
426 : usart->CR1 |= USART_CR1_PCE);
427 this->parity = parity;
428}
429
430void USART::setStopBits(int stopBits)
431{
432 miosix::FastInterruptDisableLock dLock;
433 this->stopBits = stopBits;
434 usart->CR2 &= ~USART_CR2_STOP;
435 if (stopBits == 2)
436 usart->CR2 |= USART_CR2_STOP_1;
437}
438
439void USART::setOversampling(bool oversampling)
440{
441 miosix::FastInterruptDisableLock dLock;
442 this->over8 = oversampling;
443 (oversampling ? usart->CR1 |= USART_CR1_OVER8
444 : usart->CR1 &= ~USART_CR1_OVER8);
445}
446
447void USART::setBaudrate(int baudrate)
448{
449 /*
450 * Baudrate setting:
451 * fixed point: mantissa first 12 bits, other 4 bits are the fixed point
452 * value of the fraction.
453 * - if over8==0 => DIV_Fraction[3:0] (all fraction used)
454 * USART_DIV = f/(16*baud)
455 * - if over8==1 => 0+DIV_Fraction[2:0] (first bit of fraction is 0)
456 * USART_DIV = f/(8*baud)
457 */
458 miosix::InterruptDisableLock dLock;
459
460 // USART1 and USART6 are always connected to the APB2, while all the others
461 // UART/USART peripherals are always connected to APB1
463 (id == 1 || id == 6 ? ClockUtils::APB::APB2 // High speed APB2
464 : ClockUtils::APB::APB1)); // Low speed APB1
465
466 // <<4 in order to shift to left of 4 positions, to create a fixed point
467 // number of 4 decimal digits /8 == >>3 in order to divide per 8 (from the
468 // formula in the datasheet). So it should be << 1 but, in order to make the
469 // approximation later, we shift it of 2 positions
470 uint32_t brr = ((f << 2) / (((int)baudrate * (over8 ? 1 : 2))));
471
472 // rounding to the nearest
473 usart->BRR = (brr / 2) + (brr & 1);
474
475 this->baudrate = baudrate;
476}
477
478bool USART::readImpl(void* buffer, size_t nBytes, size_t& nBytesRead,
479 const bool blocking, std::chrono::nanoseconds timeout)
480{
481 miosix::Lock<miosix::FastMutex> l(rxMutex);
482
483 char* buf = reinterpret_cast<char*>(buffer);
484 size_t result = 0;
485 error = false;
486 // Whether we timed out while waiting
487 bool timedOut = false;
488
489 miosix::FastInterruptDisableLock dLock;
490 for (;;)
491 {
492 // Try to get all the data possible from the queue
493 for (; result < nBytes; result++)
494 {
495 if (!rxQueue.tryGet(buf[result]))
496 break;
497
498 // This is here just not to keep IRQ disabled for the whole loop
499 miosix::FastInterruptEnableLock eLock(dLock);
500 }
501
502 // If blocking, we are waiting for at least one byte of data before
503 // returning. If not blocking, in the case the bus is idle we return
504 // anyway
505 if ((result == nBytes) || (idle && (!blocking || (result > 0))) ||
506 timedOut)
507 {
508 break;
509 }
510
511 // Wait for data in the queue
512 do
513 {
514 rxWaiter = miosix::Thread::IRQgetCurrentThread();
515
516 if (timeout == std::chrono::nanoseconds::zero())
517 {
518 miosix::Thread::IRQenableIrqAndWait(dLock);
519 }
520 else
521 {
522 int64_t wakeup = add_sat(miosix::IRQgetTime(), timeout.count());
523 auto waitResult =
524 miosix::Thread::IRQenableIrqAndTimedWait(dLock, wakeup);
525
526 if (waitResult == miosix::TimedWaitResult::Timeout)
527 {
528 // De-register from wakeup by the IRQ
529 rxWaiter = nullptr;
530 // Make the outer for-loop quit after reading data from the
531 // rxQueue, or we'd end up waiting again
532 timedOut = true;
533 }
534 }
535 } while (rxWaiter);
536 }
537 nBytesRead = result;
538
539 return (result > 0) && !timedOut;
540}
541
542void USART::write(const void* buffer, size_t nBytes)
543{
544 miosix::Lock<miosix::FastMutex> l(txMutex);
545
546 // TODO: Use the send complete interrupt in order not to have a busy while
547 // loop waiting
548 const char* buf = reinterpret_cast<const char*>(buffer);
549 size_t i;
550 for (i = 0; i < nBytes; i++)
551 {
552#ifndef _ARCH_CORTEXM7_STM32F7
553 while ((usart->SR & USART_SR_TXE) == 0)
554 ;
555 usart->DR = *buf++;
556#else
557 while ((usart->ISR & USART_ISR_TXE) == 0)
558 ;
559 usart->TDR = *buf++;
560#endif
561 }
562}
563
564void USART::writeString(const char* buffer)
565{
566 int i = 0;
567 miosix::Lock<miosix::FastMutex> l(txMutex);
568
569 // Send everything, also the ending '\0' character
570#ifndef _ARCH_CORTEXM7_STM32F7
571 usart->DR = *buffer;
572#else
573 usart->TDR = *buffer;
574#endif
575
576 i++;
577
578 while (*buffer != '\0')
579 {
580 buffer++;
581
582#ifndef _ARCH_CORTEXM7_STM32F7
583 while (!(usart->SR & USART_SR_TXE))
584 ;
585 usart->DR = *buffer;
586#else
587 while ((usart->ISR & USART_ISR_TXE) == 0)
588 ;
589 usart->TDR = *buffer;
590#endif
591
592 i++;
593 };
594}
595
596bool USART::writeFile(const std::string& fileName)
597{
598 std::ifstream file(fileName, std::ifstream::binary);
599 std::vector<uint8_t> buffer(1024, 0); // 1024 Bytes buffer.
600
601 if (!file.is_open())
602 {
603 LOG_ERR(logger, "Failed to open file {}", fileName);
604 return false;
605 }
606
607 while (file.read(reinterpret_cast<char*>(buffer.data()), buffer.size()))
608 {
609 std::streamsize s = file.gcount();
610 if (s > 0)
611 write(buffer.data(), static_cast<size_t>(s));
612 }
613 return true;
614}
615
617{
618 char buf[INTERNAL_QUEUE_LENGTH];
619 rxQueue.reset();
620 while (read(buf, INTERNAL_QUEUE_LENGTH))
621 ;
622 rxQueue.reset();
623}
624
626 : USARTInterface(usart, baudrate)
627{
628 if (this->id < 1 || this->id > 4)
629 {
630 LOG_ERR(logger, "USART selected not supported for STM32SerialWrapper!");
631 D(assert(false &&
632 "USART selected not supported for STM32SerialWrapper!"));
633 }
634
635 // Creates and adds the serial port to the devices
636 this->serial = new miosix::STM32Serial(id, baudrate);
637
638 if (!serialCommSetup())
639 {
641 "[STM32SerialWrapper] can't initialize serial communication!");
642 D(assert(false &&
643 "[STM32SerialWrapper] Error : can't initialize serial "
644 "communication!\n"));
645 }
646}
647
649 miosix::GpioPin tx, miosix::GpioPin rx)
650 : USARTInterface(usart, baudrate)
651{
652 if (this->id < 1 || this->id > 4)
653 {
654 LOG_ERR(logger, "USART selected not supported for STM32SerialWrapper!");
655 D(assert(false &&
656 "USART selected not supported for STM32SerialWrapper!"));
657 }
658
659 // Creates and adds the serial port to the devices
660 this->serial = new miosix::STM32Serial(id, baudrate, tx, rx);
661
662 if (!serialCommSetup())
663 {
665 "[STM32SerialWrapper] can't initialize serial communication!");
666 D(assert(false &&
667 "[STM32SerialWrapper] Error : can't initialize serial "
668 "communication!\n"));
669 }
670}
671
673{
674 miosix::intrusive_ref_ptr<miosix::DevFs> devFs =
675 miosix::FilesystemManager::instance().getDevFs();
676 close(fd);
677 devFs->remove(serialPortName.c_str());
678}
679
680bool STM32SerialWrapper::serialCommSetup()
681{
682 // Adds a device to the file system
683 if (!miosix::FilesystemManager::instance().getDevFs()->addDevice(
684 serialPortName.c_str(),
685 miosix::intrusive_ref_ptr<miosix::Device>(serial)))
686 {
687 return false;
688 }
689
690 // Path string "/dev/<name_of_port>" for the port we want to open
691 std::string serialPortPath = "/dev/" + serialPortName;
692
693 // Open serial port
694 fd = open(serialPortPath.c_str(), O_RDWR);
695
696 if (fd <= -1)
697 {
698 TRACE("Cannot open %s\n", serialPortPath.c_str());
699 return false;
700 }
701
702 return true;
703}
704
705bool STM32SerialWrapper::readImpl(void* buffer, size_t nBytes,
706 size_t& nBytesRead, const bool blocking,
707 std::chrono::nanoseconds timeout)
708{
709 // non-blocking read not supported in STM32SerialWrapper
710 if (!blocking)
711 {
713 "STM32SerialWrapper::read doesn't support non-blocking read");
714 D(assert(false &&
715 "STM32SerialWrapper::read doesn't support non-blocking read"));
716 }
717
718 // Timeout is not supported on STM32SerialWrapper, timeout is always set to
719 // std::chrono::nanoseconds::zero() unless specified by the user
720 if (timeout != std::chrono::nanoseconds::zero())
721 {
723 "STM32SerialWrapper::read doesn't support timeout on read");
724 D(assert(false &&
725 "STM32SerialWrapper::read doesn't support timeout on read"));
726 }
727
728 size_t n = ::read(fd, buffer, nBytes);
729 nBytesRead = n;
730
731 return (n > 0);
732}
733
734void STM32SerialWrapper::write(const void* buffer, size_t nBytes)
735{
736 ::write(fd, buffer, nBytes);
737}
738
739void STM32SerialWrapper::writeString(const char* buffer)
740{
741 // strlen + 1 in order to send the '/0' terminated string
742 ::write(fd, buffer, strlen(buffer) + 1);
743}
744
745} // namespace Boardcore
void __attribute__((naked)) CAN1_RX0_IRQHandler()
#define TRACE(...)
Definition Debug.h:58
#define D(x)
Definition Debug.h:57
#define LOG_ERR(logger,...)
Boardcore::USART * ports[N_USART_PORTS]
< Pointer to serial port classes to let interrupts access the classes
Definition USART.cpp:39
USART_TypeDef USARTType
Definition USART.h:36
void write(const void *buf, size_t nBytes)
Blocking write operation.
Definition USART.cpp:734
STM32SerialWrapper(USARTType *usart, int baudrate)
Initializes the serialPortName and initializes the default pins, which are:
Definition USART.cpp:625
~STM32SerialWrapper()
Removes the device from the list of the devices and closes the file of the device.
Definition USART.cpp:672
void writeString(const char *buffer)
Write a string to the serial, comprising the '\0' character.
Definition USART.cpp:739
Driver for STM32F4 low level USART/UART peripheral.
Definition USART.h:170
void setParity(ParityBit pb)
Set the presence of the parity in the data sent.
Definition USART.cpp:422
void IRQhandleInterrupt()
Interrupt handler that deals with receive and idle interrupts.
Definition USART.cpp:311
void setWordLength(WordLength wl)
Set the length of the word to 8 or to 9.
Definition USART.cpp:414
void writeString(const char *buffer)
Write a string to the serial, comprising the '\0' character.
Definition USART.cpp:564
void clearQueue()
Clears the rxQueue.
Definition USART.cpp:616
bool read(void *buffer, size_t nBytes)
Non-blocking read operation to read nBytes or till the data transfer is complete.
Definition USART.h:234
bool writeFile(const std::string &fileName)
Given a filename, uses the USART interface to stream the file in 1KB chunks.
Definition USART.cpp:596
USART(USARTType *usart, int baudrate, unsigned int queueLen=INTERNAL_QUEUE_LENGTH)
Automatically enables the peripheral and timer peripheral clock.
Definition USART.cpp:360
void write(const void *buf, size_t nBytes)
Blocking write operation.
Definition USART.cpp:542
void setStopBits(int stopBits)
Set the number of stop bits.
Definition USART.cpp:430
void setOversampling(bool oversampling)
Sets the Over8 bit.
Definition USART.cpp:439
void setBaudrate(int baudrate)
Set the baudrate in the BRR register.
Definition USART.cpp:447
~USART() override
Disables the flags for the generation of the interrupts, the IRQ from the NVIC, the peripheral and re...
Definition USART.cpp:400
Abstract class that implements the interface for the USART/UART serial communication.
Definition USART.h:70
USARTInterface(USARTType *usart, int baudrate)
Constructor of the USART in order to assign usart and baudrate.
Definition USART.cpp:241
IRQn_Type irqn
IRQ number.
Definition USART.h:153
virtual ~USARTInterface()=0
Definition USART.cpp:309
std::string serialPortName
Definition USART.h:154
bool enablePeripheralClock(void *peripheral)
Enables a peripheral clock source from the APB1 and APB2 peripheral buses.
Definition ClockUtils.h:155
uint32_t getAPBPeripheralsClock(APB bus)
Computes the output clock frequency for peripherals on the given APB.
Definition ClockUtils.h:73
This file includes all the types the logdecoder script will decode.
constexpr auto add_sat(T x, T y) noexcept -> typename std::enable_if_t< std::is_integral< T >::value, T >
Computes the saturating addition x + y for integral types.
Definition Numeric.h:43