|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | #include "verilator_spi_interface.h" | 
|  |  | 
|  | #include <fcntl.h> | 
|  | #include <termios.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <cstring> | 
|  | #include <iostream> | 
|  | #include <string> | 
|  |  | 
|  | namespace opentitan { | 
|  | namespace spiflash { | 
|  | namespace { | 
|  |  | 
|  | // Required delay to synchronize transactions with simulation environment. | 
|  | // TODO: If transmission is not successful, adapt this by an argument. | 
|  | constexpr int kWriteReadDelay = 20000000; | 
|  |  | 
|  | // Configure |fd| as a serial port with baud rate 9600. | 
|  | bool SetTermOpts(int fd) { | 
|  | struct termios options; | 
|  | if (tcgetattr(fd, &options) != 0) { | 
|  | return false; | 
|  | } | 
|  | cfmakeraw(&options); | 
|  | // The current Verilator configuration uses 9600 baud rate. | 
|  | if (cfsetispeed(&options, B9600) != 0) { | 
|  | return false; | 
|  | } | 
|  | if (cfsetospeed(&options, B9600) != 0) { | 
|  | return false; | 
|  | } | 
|  | if (tcsetattr(fd, TCSANOW, &options) != 0) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Returns file handle on success, or nullopt on failure. This function | 
|  | // configures de file handle to behave as a serial port with baud rate 9600, | 
|  | // which is the baud rate supported by Verilator. | 
|  | int OpenDevice(const std::string &filename) { | 
|  | int fd = open(filename.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC); | 
|  | if (fd < 0) { | 
|  | std::cerr << "Failed to open device: " << filename << std::endl; | 
|  | return fd; | 
|  | } | 
|  | if (!SetTermOpts(fd)) { | 
|  | close(fd); | 
|  | return -1; | 
|  | } | 
|  | return fd; | 
|  | } | 
|  |  | 
|  | // Reads |size| bytes into |rx| buffer from |fd|. Returns the number of bytes | 
|  | // read. | 
|  | int ReadBytes(int fd, uint8_t *rx, size_t size) { | 
|  | size_t bytes_read = 0; | 
|  | while (bytes_read != size) { | 
|  | size_t read_size = read(fd, &rx[bytes_read], size - bytes_read); | 
|  | switch (read_size) { | 
|  | case -1: | 
|  | if (errno == EAGAIN || errno == EWOULDBLOCK) { | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | bytes_read += read_size; | 
|  | } | 
|  | } | 
|  | return bytes_read; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | VerilatorSpiInterface::~VerilatorSpiInterface() { | 
|  | if (fd_ != -1) { | 
|  | close(fd_); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool VerilatorSpiInterface::Init() { | 
|  | fd_ = OpenDevice(spi_filename_); | 
|  | if (fd_ < 0) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerilatorSpiInterface::TransmitFrame(const uint8_t *tx, uint8_t *rx, | 
|  | size_t size) { | 
|  | size_t bytes_written = write(fd_, tx, size); | 
|  | if (bytes_written != size) { | 
|  | std::cerr << "Failed to write bytes to spi interface. Bytes written: " | 
|  | << bytes_written << " expected: " << size << std::endl; | 
|  | return false; | 
|  | } | 
|  | usleep(kWriteReadDelay); | 
|  | size_t bytes_read = ReadBytes(fd_, rx, size); | 
|  | if (bytes_read < size) { | 
|  | std::cerr << "Failed to read bytes from spi interface. Bytes read: " | 
|  | << bytes_read << " expected: " << size << std::endl; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | }  // namespace spiflash | 
|  | }  // namespace opentitan |