-
Notifications
You must be signed in to change notification settings - Fork 13
ot_spi_device,ot_spi_host: Bugfix + Refactoring + Passthrough mode implementation
#255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: ot-9.2.0
Are you sure you want to change the base?
ot_spi_device,ot_spi_host: Bugfix + Refactoring + Passthrough mode implementation
#255
Conversation
b7c3b04 to
2e2300b
Compare
2264c77 to
f1a68a2
Compare
hw/opentitan/ot_spi_device.c
Outdated
| bool loop; /* Keep reading the buffer if end is reached */ | ||
| bool watermark; /* Read watermark hit, used as flip-flop */ | ||
| bool new_cmd; /* New command has been pushed in current SPI transaction */ | ||
| bool cmd_addr_swap_en; /* Passthrough mode - address swap enabled */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe group all the passthrough related value into a structure. (cmd....?)
This would also to clear the whole thing at once in ot_spi_device_passthrough_unmatched_command_params
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If not grouped in a structure no big deal but it seems these values are not reset yet.
f1a68a2 to
6e8f034
Compare
e8ef6d7 to
ad5c39c
Compare
ot_spi_device: Refactoring + Passthrough mode implementationot_spi_device,ot_spi_host: Refactoring + Passthrough mode implementation
ca61a08 to
28ffc3c
Compare
|
I have discovered that OT SPI Device is not the only device controlling this SPI bus, the bus is actually shared by OT SPI Host 0 in Earlgrey, and OT SPI Host is bypassed entirely when Passthrough mode is enabled. This seems to only be documented here. As such this PR has been reworked a bit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
quick review.
28ffc3c to
a49e9bf
Compare
a49e9bf to
3a643ee
Compare
ot_spi_device,ot_spi_host: Refactoring + Passthrough mode implementationot_spi_device,ot_spi_host: Bugfix + Refactoring + Passthrough mode implementation
3a643ee to
05aa2a7
Compare
05aa2a7 to
6fc1a21
Compare
|
Just missing some checks in OT SPI Host to inhibit its activity when Passthrough on SPI Device is enabled, which should be a very pathological situation. Otherwise ready for review and nits up to now. |
Depends on lowRISC/qemu#255 Signed-off-by: Alice Ziuziakowska <[email protected]>
Depends on lowRISC/qemu#255 Signed-off-by: Alice Ziuziakowska <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two small features missing
- complete cleanup of passthrough variables from
reset_enter - add
LOG_GUEST_ERRORmessage when the SPI host is attempting to drive its /CS line or data lines whenpassthrough_enis enabled. I guess it could be implemented by trackingpassthrough_enwhenever it attempts to change its first /CS line.
hw/opentitan/ot_spi_host.c
Outdated
| { | ||
| /* Passthrough is not implemented if SPI Host has more than one CS line */ | ||
| if (s->num_cs != 1u) { | ||
| trace_ot_spi_host_passthrough_unimplemented(s->ot_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit pick: I wonder if the name of the trace is not a bit confusing, as unimplemented is usually ... used to report something that is not -yet- implemented, whereas here it is the HW that does not support this feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a LOG_GUEST_ERROR instead maybe? The software should know if there is more than one CS on the hardware it is running on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it could be understood this way yes.
In this case, I think it would great to also add a boolean flag so that this error message is only emitted once (as we do for OT_UNIMP) so that the error log is not flooded with the same message for every attempt to transmit a byte or drive the /CS line.
hw/opentitan/ot_spi_host.c
Outdated
|
|
||
| /* | ||
| * Real HW muxes the CS line based on Passthrough Enable, but it doesn't | ||
| * make sense for a transfer to be handled by both SPI Host and SPI Device, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some LOG_GUEST_ERROR is definitely needed here:
It does not make sense, but it can happen (so it will). It's quite trivial to have two unrelated tasks that try to access the same HW resource. Especially with one driver managing the SPI device while another managing the SPI host.
If the HW does not provide a mean to manage exclusive access to the SPI bus from the SW, QEMU should neither do it, but it should inform the user that something unexpected is going on.
The VM not only aims at emulating the HW but also to give better insight of how the HW is driven to help fixing SW errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The means of exclusive access to the SPI bus is the Passthrough Enable signal, which controls the mux. I thought it would be really pathological for something to start on the SPI Host, have SPI device assert chip select too, then enable Passthrough so that it is controlling the bus for the remainder of the same transfer, so that is what I aimed to try and prevent. Do you mean that I should relax this and add a GUEST_ERROR warning if changing the Passthrough Enable when both have a Chip Select active?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how to define pathological here, but a simple SW bug would be enough.
It is a somewhat weird design to have one HW device silently using another HW device with no way to control this from SW (i.e. through a register in SPI host that manages the mux). That's what pinmuxes are for (among other things)...
What I meant is that if SPI device try to set passthrough_en while SPI host is using the same bus (i.e. it has asserted the /CS line), a LOG_GUEST_ERROR should be emitted, and it should take priority over SPI host, potentially trashing the flash.
Conversely, if the SPI host is trying to assert /CS while passthough_en is enabled, a LOG_GUEST_ERROR should be emitted. Moreover, in this case, its /CS action or ssi_transfer requests should be discarded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I now track the passthrough chip select, passthrough enable, and chip select from the SPI Host, and mux those faithfully onto the CS[0] IRQ. I've added guest errors around any suspicious transitions.
a847e12 to
1ef99aa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the work on this @ziuziakowska, it looks great to me and matches the HW implementation more closely now.
I agree with @rivos-eblot's remaining comments, but I'm happy to approve when those (and my new comments) are addressed.
As one final suggestion, it would also be great if you could rebase on #277 and then update the SPI device documentation to briefly note that passthrough is supported, and any known limitations (e.g. dummy cycles rounded up)?
| default: | ||
| qemu_log_mask(LOG_UNIMP, "%s: %s: unsupported mode\n", __func__, | ||
| s->ot_id); | ||
| ibex_irq_raise(&s->passthrough_en); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this should transition the bus state to discard?
In reality, disabling (or changing mode) mid transmission could be a huge edge case that is not supported - it is understandable to not handle that in this PR, but it would be good to put a TODO or an UNIMP on this case.
Both EG and DJ use E/I SRAM now Signed-off-by: Alice Ziuziakowska <[email protected]>
Signed-off-by: Alice Ziuziakowska <[email protected]>
The bit-fields previously used here are just advisory/convention, the whole 22-bit field is RW and software maintained. This is also expected behaviour for spi_passthru_test. Signed-off-by: Alice Ziuziakowska <[email protected]>
The bytes are received over the wire and stored in the buffer most-significant byte first (big-endian), so the dummy byte is the last byte not the first. Signed-off-by: Alice Ziuziakowska <[email protected]>
Expected by spi_passthru_test, see: https://github.com/lowRISC/opentitan/blob/master/sw/host/tests/chip/spi_device/src/spi_passthru.rs#L70-L80 Signed-off-by: Alice Ziuziakowska <[email protected]>
…ALUE Name is more verbose but matches the usage better, as in `ot_spi_host` Signed-off-by: Alice Ziuziakowska <[email protected]>
This commit simplifies command slot definitions and the associated state and control flow. The HW CFG and the HW STA commands have been combined into just HW commands, as in flash mode they are handled in hardware by some way and should therefore share the same data path. For passthrough mode, a subset of these HW commands can be intercepted (all except for WREN and WRDI). As the matched command slot number determines whether the command is a SW or HW command, the `OtSpiFlashCommand` enum has been removed in favour of using `is_sw_command` to return the boolean. Command slot matching has been brought out into its own function, `match_command_slot`, which returns whether an opcode was matched in the command info registers. The decoded slot number is only valid if this returns true. Also a bug is fixed in `ot_spi_device_exec_command` where hardcoded opcodes for read commands were used instead of the opcodes in the associated command slot. Signed-off-by: Alice Ziuziakowska <[email protected]>
Signed-off-by: Alice Ziuziakowska <[email protected]>
... and removes `OtSpiDeviceAddrMode`. `ot_spi_device_get_command_address_size` now returns the size of the address field in the current command, using the value in `cmd_info` and the current 4B enable state. Signed-off-by: Alice Ziuziakowska <[email protected]>
Signed-off-by: Alice Ziuziakowska <[email protected]>
Signed-off-by: Alice Ziuziakowska <[email protected]>
This implements a passthrough enable and chip select IRQ and a method to interact with the downstream SPI bus, for use by upstream SPI Device. Signed-off-by: Alice Ziuziakowska <[email protected]>
1ef99aa to
07a9072
Compare
| static uint8_t ot_spi_host_downstream_transfer(OtSPIHostState *s, uint8_t tx) | ||
| { | ||
| if (!ot_spi_host_supports_passthrough(s)) { | ||
| qemu_log_mask( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be useful to add a boolean value to only emit this error (passthrough triggerred) once per reset session, to avoid flooding the error stream (one per byte...)
The same boolean could be re-used with all siblings errors (/CS, EN)
It could be a bitmask of errors, as there are several ones to track
| if (!ot_spi_host_supports_passthrough(s) || !s->passthrough_en) { | ||
| rx = (uint8_t)ssi_transfer(s->ssi, tx); | ||
| } else { | ||
| qemu_log_mask( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another boolean for this error (host-triggered) would be nice.
| return s->num_cs == 1u; | ||
| } | ||
|
|
||
| static void ot_spi_host_update_muxed_chip_select(OtSPIHostState *s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe renamed as ot_spi_host_update_muxed_cs0 to match host_cs0 and since it is specific to CS0
| uint8_t rx = SPI_DEFAULT_TX_RX_VALUE; | ||
|
|
||
| if (s->fsm.output_en) { | ||
| if (!ot_spi_host_supports_passthrough(s) || !s->passthrough_en) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: might be worth adding some inline func
static inline bool ot_spi_host_passthough_active(const OtSpiHostState *s)
{
return ot_spi_host_supports_passthrough(s) && s->passthrough_en;
}as this test is used several times and it would make the syntax a bit lighter
|
|
||
| if (!s->fsm.transaction) { | ||
| s->fsm.transaction = true; | ||
| if (ot_spi_host_supports_passthrough(s) && s->active.cmd.cs == 0u && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is s->active.cmd.cs == 0u required since ot_spi_host_supports_passthrough(s) should be false in case SPI host supports multiple CS, i.e. CS != 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not too familiar with SPI Host, so I am not sure if you couldn't accidentally get a non-zero CS number into this part of the FSM. If the value is bounded by num_cs at all times then I will remove this check as it would be redundant then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is handled here:
if (!(csid < s->num_cs)) {
/* CSID exceeds max num_cs */
qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: invalid csid: %u\n",
__func__, s->ot_id, csid);
REG_UPDATE(s, ERROR_STATUS, CSIDINVAL, 1u);
csid = 0;
}
CmdFifoSlot slot = {
.opts = s->regs[R_CONFIGOPTS],
.command = val32,
.cs = csid,
.id = s->last_command_id++,
};If the guest SW uses an value not supported by the HW, the execution resumes with CS == 0
It is very weird, but this follow the HW (at least the last time I dug into the RTL :-))
If I'm not mistaken, when cs_num is 1, cs is always 0; when cs_num is greater, passthrough is not supported.
| qemu_log_mask(LOG_GUEST_ERROR, | ||
| "%s: R/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__, | ||
| addr, TPM_REG_NAME(reg)); | ||
| "%s: %s: R/O register 0x%02" HWADDR_PRIx " (%s)\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I got tired on this syntax in other files, so I've started to use something more compact (less lines) and fully valid with OT:
"%s: %s: .. 0x%02x (%s)\n",
__func__, s->ot_id, (uint32_t)addr, TPM_REG_NAME(reg));
|
Note: you will need to update |
This adds the corresponding out IRQs to OT SPI Device, as well as a property that contains the downstream OT SPI Host. Signed-off-by: Alice Ziuziakowska <[email protected]>
…IRQs Signed-off-by: Alice Ziuziakowska <[email protected]>
This commit implements the Passthrough mode on SPI Device. Passthrough mode allows the device to act as a proxy for a downstream flash device, optionally intercepting commands to send back its own values, filtering commands sent downstream, or transparently translating address and payload bytes. This also "implements" the Disabled mode, which discards all bytes sent to the device. Signed-off-by: Alice Ziuziakowska <[email protected]>
07a9072 to
74110d6
Compare
This PR implements the Passthrough mode for OT SPI Device and any required changes in OT SPI Host. Passthrough mode allows for external SPI transfers to be passed through to a downstream device, and optionally intercepted or modified by OT SPI Device.
In HW, OT SPI Device and OT SPI Host are linked by their SPI bus and a wire that represents whether the Device is in Passthrough mode. If Passthrough mode is enabled, the SPI Host is entirely bypassed and SPI Device has full control over the bus, which it uses to talk to the same downstream flash as SPI Host. See Documentation reference, RTL.
This passthrough mechanism is only implemented by OT SPI Host if it has one CS line: RTL
The structure of the PR is as follows:
ot_spi_devicefor the Passthrough implementation.ot_spi_host- two GPIOs are used to communicate Passthrough enabled and the Chip Select as driven by OT SPI Device, OT SPI Device and OT SPI Host are then linked together.ot_spi_device.I have tried to handle most corner-cases I can think of, but some conflicting configuration options might not behave as expected.
Unimplemented features:
Testing:
This implementation has been tested against
spi_passthru_test. See lowRISC/opentitan#28649 for the changes required in OpenTitan. To test manually the steps are:dd if=/dev/zero of=<flash> bs=1M count=32spi_passthru_test, with these additional flags:-global ot-earlgrey-board.spiflash0=w25q256 -drive if=mtd,file=<flash>,format=raw,bus=0