Log Engine

1. Overview

The Log Engine sends memory-resident messages to a UART without a CPU copying each byte. Software supplies one memory region, divided into 16 slots, and submits each message by writing its slot’s length. The engine selects pending slots in round-robin order and completes one message before moving to the next.

A 64-bit AXI4-Lite read path fetches message data into a FIFO. A separate 32-bit AXI4-Lite write path sends one character at a time to a fixed UART address, paced by uart_tx_ready_i. The register interface is also 32-bit AXI4-Lite. Fetch addresses are 56 bits; UART addresses are 32 bits. The Log Engine section of the Programmer’s Guide gives the configuration, submission and recovery procedures.

uart_log_engine_wrap: a register demux to the UART, the log engine, the control registers and an error subordinate; the log engine’s fetch path to memory and its write path through an AXI-Lite mux into the UART 16550
Figure 1. SMC log-engine wrapper and data paths

Figure 1 shows the SMC wrapper and its memory-to-UART data paths. Parentheses after an instance name give its module or, for a block taken from another project, that project: the demuxes, the mux and the axi_lite_from_mem converters come from PULP, and u_uart_core, u_arbiter_tree and u_rdata_fifo from OpenTitan. uart_log_engine_wrap runs on one clock, clk_i. Its register port, with 32-bit address and data, comes from uart_wrap, and the demux u_csr_axi_lite_demux decodes it by base and size onto four maps: the UART, the log engine, the control block and an error slave that answers DECERR. The control block u_uart_log_engine_ctrl_reg, generated by PeakRDL from uart_log_engine_ctrl.rdl, drives uart_en_o from CTRL.UART_EN.

The UART register path and the log engine’s write path share the UART’s register port through the mux u_log_write_axi_lite_mux: port 0 carries register accesses and port 1 the log engine’s writes. Inside u_uart_16550, the demux u_axi_lite_demux selects one of three PeakRDL register blocks. While LCR.DLAB is set, DLL and DLM go to the divisor-latch block (dl); writes to THR and FCR go to the write-only block (main_wo); everything else goes to the main block. u_uart_core holds the TX and RX FIFOs, the transmitter and the receiver, and passes uart_rx_i and the modem inputs through two-flop synchronizers, marked by the synchronizer badge on its edge.

The dashed outline marks the log engine, which exists only when GEN_LOG_ENGINE is set; without it the log-fetch port is driven to zero and log_engine_irq_o is low. u_log_engine_reg, generated from log_engine.rdl, holds the 16 LOG_CTRL registers and the interrupt registers, and drives log_engine_irq_o. u_arbiter_tree picks a log entry whose LOG_LEN is non-zero and gives its index and length to the log fetch FSM. The fetch FSM reads 64-bit words through the axi_lite_from_mem converter u_axil_lite_from_log_fetch_fsm on the log-fetch port, which has 56-bit address and 64-bit data, into u_rdata_fifo, whose depth is FIFO_DEPTH (4 in the SMC). The log write FSM takes one byte at a time from the FIFO and writes it, through a second converter, u_axi_lite_from_log_write_fsm, to LOG_WRITE_ADDR over a 32-bit AXI4-Lite port. It waits for uart_txrdy_o before each write.

2. Operation

2.1. Memory Slots and Lengths

LOG_REGION_ADDR specifies the start of a shared region. LOG_REGION_SIZE specifies its total size in bytes. Use an 8-byte-aligned base and a nonzero size aligned to 128 bytes, up to 512 KiB. The region is divided equally among 16 slots; it does not provide independently sized buffers.

For supported settings, slot i starts at:

LOG_REGION_ADDR + (LOG_REGION_SIZE / 16) * i

A nonzero LOG_CTRL[i].LOG_LEN makes that slot pending. The length is in bytes and remains the submitted value until hardware clears it on completion; it is not a bytes-remaining counter. Keep the buffer and its configuration unchanged until completion.

For oversized regions, hardware clamps the effective size to 512 KiB. For a size not aligned to 128 bytes, it divides the effective size by 16 and rounds each slot’s transferable capacity down to a multiple of eight. Requested lengths above that capacity are clamped. A zero-capacity slot completes without a transfer. Software should validate size and length instead of relying on truncation.

2.2. Arbitration and Data Transfer

The arbiter selects among nonzero lengths and advances when the selected message completes. Scheduling is per message, not per byte. A stalled UART or bus response holds up that message and all later entries; round-robin selection does not impose a completion-time bound.

The fetch FSM issues single-beat 64-bit reads and waits for each response. A FIFO_DEPTH-entry FIFO buffers the returned words. The final read still fetches a full eight-byte word when the message length is not a multiple of eight, so the whole fetched word must be readable within the slot.

The write FSM sends bytes in increasing address order, least-significant byte first within each fetched word. It waits for FIFO data and uart_tx_ready_i, writes the character in data bits [7:0] to LOG_WRITE_ADDR, then waits for the response. The current implementation sets all four write strobes; the UART target must accept that access.

Completion clears the selected length and advances arbitration. It means the write responses have returned, not that the UART has finished shifting the final character onto its serial output.

2.3. Errors and Interrupts

INTR_STATUS.LOG_FETCH_ERR (bit 0) and LOG_WRITE_ERR (bit 4) latch bus response errors whether or not their interrupts are enabled. INTR_ENABLE masks the corresponding contributions to irq_o. Write 1 to a status bit to clear it; INTR_TEST can force either condition.

The FSMs advance on error responses as well as successful responses. There is no automatic retry or per-slot success flag, and a cleared length alone does not prove successful delivery. Software must inspect error status and choose recovery appropriate to its logging requirements.

2.4. Reset and Disable

All logic uses clk_i. Active-low rst_ni asynchronously resets the registers, state machines, counters and FIFO. Reset release timing is the integrator’s responsibility; the module does not synchronize it internally.

Clearing CTRL.EN returns the fetch/write FSMs and counters to idle and clears the FIFO. It does not clear configuration or pending lengths, and it does not reset the AXI adapters. Disabling an active transfer is not a guaranteed drain or clean cancellation. Configure while idle; following an aborted transfer, reconcile outstanding bus traffic before reusing buffers or restarting the engine.

3. Interface

The parameters and ports of log_engine are listed in log_engine.

Table 1. Log-engine configuration
Setting Value Source

FIFO_DEPTH

4 by default

Module parameter: buffered 64-bit words.

NumLogEntries

16

Package constant and generated register inventory.

Maximum region

512 KiB

Package constant MaxLogRegionSize.

Fetch address/data

56/64 bits

Package types.

Write address/data

32/32 bits

Package types.

CSR data

32 bits

Register interface; address width comes from the generated map.

A shared clk_i and active-low asynchronous rst_ni drive all interfaces and internal logic. The CSR AXI4-Lite slave (csr_axil_req_i / csr_axil_resp_o) carries register traffic; two AXI4-Lite initiators carry memory reads (log_fetch_axil_req_o / log_fetch_axil_resp_i) and UART writes (log_write_axil_req_o / log_write_axil_resp_i). Both initiator adapters use PROT = 3’b010 (non-secure, unprivileged data) and allow one outstanding request each. They issue no bursts.

uart_tx_ready_i permits the write FSM to request the next character transfer. The UART integration supplies readiness in the engine’s clock domain and must account for writes already accepted by the bus. See Section 2.2 for the write data and strobe behavior.

irq_o is the active-high OR of enabled, latched fetch and write error status.

4. Memory Map and Register Reference

4.1. Memory Map

Offsets below are relative to the Log Engine register bank. All registers reset to zero. The generated map gives field access and side effects.

Table 2. Log-engine register summary
Register Offset Purpose

CTRL

0x00

Engine enable.

LOG_REGION_SIZE

0x04

Total region size in bytes.

LOG_REGION_ADDR

0x08/0x0C

Low/high halves of the 56-bit fetch base.

LOG_WRITE_ADDR

0x10

UART write address.

INTR_STATUS

0x14

Latched fetch/write errors; write 1 to clear.

INTR_ENABLE

0x18

Error interrupt enables.

INTR_TEST

0x1C

Force error status for interrupt testing.

LOG_CTRL[i]

0x40 + 4*i

Submitted byte length for slot 0–15; cleared on completion.

4.2. Detailed Register Map

Log Engine Address Map

Configuration and status registers for the UART log engine, including the log region and write pointers.

Register Arrays:

  • LOG_CTRL[16]: 16 registers, base 0x40, stride 0x4

Register List:

Table 3. Log Engine Address Map register list
AddressNameAccessDescription
0x0CTRLRWControl Register
0x4LOG_REGION_SIZERWLog Region Size Register
0x8LOG_REGION_ADDRRWLog Region Address Register
0x10LOG_WRITE_ADDRRWLog Write Address Register
0x14INTR_STATUSRWInterrupt Status Register
0x18INTR_ENABLERWInterrupt Enable Register
0x1CINTR_TESTWInterrupt Test Register
0x40 - 0x7CLOG_CTRL[16]RWLog Control Register

Register Details:

CTRL

Table 4. log_engine.CTRL fields
BitsFieldAccessResetDescription
31:1Reserved--Reserved
0ENRW0x0Log Engine Enable. When set, enables the Log Engine. When cleared, the engine
stops fetching and writing, discards buffered log data and resets transfer
progress. Configuration and pending LOG_LEN values are retained, so a pending
entry restarts from the start of its slot when the engine is enabled again.
Clearing does not wait for outstanding bus responses.

LOG_REGION_SIZE

Table 5. log_engine.LOG_REGION_SIZE fields
BitsFieldAccessResetDescription
31:20Reserved--Reserved
19:0LOG_REGION_SIZERW0x0Log region size in bytes. Supported values are at most 512 KiB and aligned
to 128 bytes (16 slots times the 8-byte fetch beat). If software programs a
larger value, RTL clamps the effective region to 512 KiB. For a value that is
not 128-byte aligned, RTL divides the effective region among the 16 slots and
rounds each slot's transferable capacity down to complete 8-byte fetch beats.
A requested LOG_LEN above that capacity is clamped to the capacity.

LOG_REGION_ADDR

Table 6. log_engine.LOG_REGION_ADDR fields
BitsFieldAccessResetDescription
63:56RESERVEDR0x0Reserved. Reads as zero; writes are ignored.
55:32LOG_REGION_ADDR_HIRW0x0Log Region Start Address High. Contains fabric address bits [55:32].
31:0LOG_REGION_ADDR_LORW0x0Log Region Start Address Low.

LOG_WRITE_ADDR

Table 7. log_engine.LOG_WRITE_ADDR fields
BitsFieldAccessResetDescription
31:0LOG_WRITE_ADDRRW0x0Log Write Address. This is the address where the log data is written to.

INTR_STATUS

Table 8. log_engine.INTR_STATUS fields
BitsFieldAccessResetDescription
31:5Reserved--Reserved
4LOG_WRITE_ERRRW1C0x0Log Write Error Interrupt. Asserted when the bus for writing log data returns
an error.
3:1Reserved--Reserved
0LOG_FETCH_ERRRW1C0x0Log Fetch Error Interrupt. Asserted when the bus for fetching log data returns
an error.

INTR_ENABLE

Table 9. log_engine.INTR_ENABLE fields
BitsFieldAccessResetDescription
31:5Reserved--Reserved
4LOG_WRITE_ERRRW0x0Log Write Error Interrupt Enable.
3:1Reserved--Reserved
0LOG_FETCH_ERRRW0x0Log Fetch Error Interrupt Enable.

INTR_TEST

Table 10. log_engine.INTR_TEST fields
BitsFieldAccessResetDescription
31:5Reserved--Reserved
4LOG_WRITE_ERRW 1P0x0Log Write Error Interrupt Test. Writing `1` forces the interrupt.
3:1Reserved--Reserved
0LOG_FETCH_ERRW 1P0x0Log Fetch Error Interrupt Test. Writing `1` forces the interrupt.

LOG_CTRL[16]

Table 11. log_engine.LOG_CTRL[16] fields
BitsFieldAccessResetDescription
31:16Reserved--Reserved
15:0LOG_LENRW0x0Log Length. Writing a nonzero value representing the log length in bytes to this field
starts the log transfer process. The field holds the requested length until the
transfer completes, when hardware clears it to zero; it is not a remaining-byte
counter. There are 16 copies of this register, each representing
a log entry. The log entries go through round robin arbitration.