Skip to content
Merged
2 changes: 1 addition & 1 deletion .gitlab-ci.d/opentitan/qemu-ot.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
variables:
BAREMETAL_REF: "a0-251104-1"
BAREMETAL_REF: "ot-251125-1"
QEMU_BUILD_OPTS: "--disable-install-blobs"

include:
Expand Down
2 changes: 1 addition & 1 deletion docs/opentitan/ot_spi_device.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ should release the /CS line, i.e. <c> should be 0.
- `p`: polarity, should match `CFG.CPOL` (not yet supported)
- `a`: phase, should match `CFG.CPHA` (not yet supported)
- `t`: tx order, see `CFG.TX_ORDER` (not yet supported)
- `r`: rx order, see `CFG.TX_ORDER` (not yet supported)
- `r`: rx order, see `CFG.RX_ORDER` (not yet supported)
- `c`: whether to keep _/CS_ low (=1) or release _/CS_ (=0) when payload has been processed. Any
SPI transaction should end with C=0 packet. However it is possible to use several SPI device
CharDev packets to handle a single SPI transaction: example: JEDEC ID w/ continuation code,
Expand Down
2 changes: 1 addition & 1 deletion docs/opentitan/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ All the OpenTitan tools and associated files are stored in the `scripts/opentita

## Installation

Most tools are implemented in Python language. They require Python 3.9 or newer.
Most tools are implemented in Python language. They require Python 3.10 or newer.

It is recommended to install Python dependencies using a [virtual environment](https://virtualenv.pypa.io).

Expand Down
13 changes: 11 additions & 2 deletions docs/opentitan/verilate.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ binaries as QEMU and comparing the outcome of each simulation environment.

````text
usage: verilate.py [-h] [-V VERILATOR] [-R FILE] [-M FILE] [-F FILE] [-O VMEM]
[-K] [-D TMP_DIR] [-c CFG] [-a PREFIX] [-C CYCLES] [-I]
[-k SECONDS] [-l] [-P FILE] [-w] [-x] [-v] [-d] [-G]
[-K] [-D TMP_DIR] [-c CFG] [-a PREFIX] [-b TCP_PORT]
[-C CYCLES] [-I] [-k SECONDS] [-l] [-P FILE] [-w] [-x] [-v]
[-d] [-G]
[ELF ...]

Verilator wrapper.
Expand All @@ -36,6 +37,8 @@ Verilator:
-a, --artifact-name PREFIX
set an alternative artifact name (default is derived
from the application name)
-b, --spi-device-bridge TCP_PORT
Create a SPI device bridge
-C, --cycles CYCLES exit after the specified cycles
-I, --show-init show initializable devices
-k, --timeout SECONDS
Expand All @@ -62,6 +65,12 @@ Extras:
* `-a` / `--artifact` all artifact files (see `-l`, `-x` and `-w`) are named after the application
name. This option specifies an alternative file prefix for all those artifacts.

* `-b` / `--spi-device-bridge` create a local server on the specified TCP port that accepts QEMU SPI
device CharDev compliant requests, translates and converts them into simplified Verilator SPI DPI
requests using its PTY SPI device channel. SDO output data received on the PTY channel are
converted back into QEMU CharDev compliant responses. This feature can be enabled to use SPI
device tests that have been designed for the QEMU VM.

* `-C` / `--cycles` abort Verilator execution after the specified count of cycles. See also the `-k`
option.

Expand Down
51 changes: 45 additions & 6 deletions python/qemu/ot/spi/spi_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class SpiDevice:
'RESET1': 0x66,
'RESET2': 0x99,
'CHECK_ANSWER': 0xca,
'CUSTOM_COMMAND': 0xed, # reserved opcode for custom commands
}
"""Supported *25 SPI data flash device commands."""

Expand All @@ -91,6 +92,7 @@ def __init__(self):
self._4ben = False
self._rev_rx = False
self._rev_tx = False
self._timeout = self.TIMEOUT

def connect(self, host: str, port: Optional[int] = None) -> None:
"""Open a connection to the remote host.
Expand Down Expand Up @@ -120,7 +122,7 @@ def connect(self, host: str, port: Optional[int] = None) -> None:
except ValueError as exc:
raise ValueError('TCP port not specified') from exc
self._socket = create_connection((host, port),
timeout=self.TIMEOUT)
timeout=self.CONN_TIMEOUT)
self._socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
elif sock_args[0] == 'unix':
self._socket = socket(AF_UNIX, SOCK_STREAM)
Expand All @@ -143,6 +145,22 @@ def disconnect(self) -> None:
self._socket.close()
self._socket = None

@property
def exchange_timeout(self) -> float:
"""Get the maximum allowed time to perform a SPI round trip.

:return: the timeout in seconds
"""
return self._timeout

@exchange_timeout.setter
def exchange_timeout(self, timeout: float) -> None:
"""Set the maximum allowed time to perform a SPI round trip.

:param timeout: the timeout in seconds
"""
self._timeout = timeout

quit = disconnect
"""Old API."""

Expand Down Expand Up @@ -207,7 +225,9 @@ def read_jedec_id(self) -> JedecId:

def read_sfdp(self, address: int = 0) -> bytes:
"""Read out the flash device SFTP descriptor."""
payload = spack('>I', address)
# SFTP command expects a dummy byte before the SFDP payload is actually
# streamed, address should be 3 byte long
payload = spack('>Ix', address)[1:]
return self.transmit(self.COMMANDS['READ_SFDP'], payload, 256)

def enable_write(self):
Expand Down Expand Up @@ -264,10 +284,9 @@ def sector_erase(self, address: int):
addr = addr[1:]
self.transmit(self.COMMANDS['SECTOR_ERASE'], addr)

def reset(self):
def reset(self, alt: bool = False):
"""Reset the flash device."""
# self.transmit(self.COMMANDS['RESET1'])
self.transmit(self.COMMANDS['RESET2'])
self.transmit(self.COMMANDS['RESET2' if not alt else 'RESET1'])

def read(self, address: int, length: int, fast: bool = False,
release: bool = True) -> bytes:
Expand Down Expand Up @@ -301,6 +320,26 @@ def read_cont(self, length: int, release: bool = True) -> bytes:
"""
return self.transmit(None, None, length, release)

def custom_command(self, address: Optional[int] = None,
payload: Optional[bytes] = None) -> bytes:
"""Execute a custom command.

:param address: optional address, sent as 3 or 4 bytes, depending on
the current mode
:param payload: optional payload
"""
if address is not None:
byte_count = 4 if self.is_4b_addr else 3
if address >= (1 << (byte_count * 8)):
raise ValueError('Cannot encode address')
addr = spack('>I', address)
if not self.is_4b_addr:
addr = addr[1:]
else:
addr = b''
payload = b''.join((addr, payload or b''))
return self.transmit(self.COMMANDS['CUSTOM_COMMAND'], addr)

def release(self) -> None:
"""Release /CS line."""
data = self._build_cs_header(0, True)
Expand Down Expand Up @@ -385,7 +424,7 @@ def _send(self, buf: bytes, release: bool = True):
def _receive(self, size: int) -> bytes:
buf = bytearray()
rem = size
timeout = now() + self.TIMEOUT
timeout = now() + self._timeout
poller = spoll()
poller.register(self._socket, POLLIN)
while rem:
Expand Down
Loading
Loading