Open GregAC opened 1 month ago
We do need to consider if the onehot mux offers the most efficient implementation for FPGA. The pinmux could end up consuming significant logic resources due to all of the muxing.
Say you have 3 inputs to select from a single 6-LUT (the architecture on the Artix 7 FPGAs) can implement the whole mux with a one-hot select and you get the output 0 when select is 0 functionality.
A 6-LUT could implement a 4 input mux with a integer select (i.e. a 2-bit select) however that doesn't give you the output 0 functionality. Unless you hardwire one of the inputs to 0 and you're back to the same thing.
At 6 inputs, you'd need 3 LUTs to implement the one hot version, (composing 2x3 input one-hot muxes with a final OR) but I think you can do it with 2 for the integer select version. One 4 input mux fed with the bottom 2 bits of the select followed by a second 3 input mux that chooses between the first mux and the other two inputs, depending on the full select signal. This can have the bonus feature of output on 0 if the unused 6 and 7 select indexes are used.
This may mean what to choose depends upon the size of the mux. We don't have loads of time to experiment here but worth building things such that it is easy to do so.
Thanks for this proposal Greg. I'm tempted to write a dedicate Python script for this, unless anyone knows of a tool that can already to most of this.
In terms of the register interface, I would like to give each pin a dedicated 8 bits. This means you can update four pins with a single 32-bit write. As you suggest writing 0 disconnects both the output and the input, so the software should zero all of the registers before starting its configuration. This is what I would propose for the encoding:
7
: output enable6
: input connected5
: reserved4-0
: pin selection (max 32 possibilities)If both 7
and 6
are zero then the value of the pin selection is ignored. If both 7
and 6
are one then also the output enable is connected to the block for full control. Since there is only one selection field it does prohibit pins from being an output of one block and an input from a separate block, but I don't see a need for this in our current design. I've kept 5
as reserved bit in case we want to extend it in the future and because I think a maximum of 32 different connections per pin is more than sufficient.
What is the use of the output enable and input connected bits? The output enable is inherent in the pin block selection. It could be used for overriding the output enable from a block but are there any use cases for this (when connected to GPIO you get output enable control anyway, when connected to I2C it's vital the I2C block gets control over output enable).
Happy to have the pins each get a dedicated 8 bits with a 5 bit selection field but not sure we've got a compelling use case for the top 3 bits yet so they should all just be reserved.
I'm tempted to write a dedicate Python script for this, unless anyone knows of a tool that can already to most of this.
Yes this was my intent, it will need a dedicated tool.
Oh and it'd be worth building this so it uses the OpenTitan prim_pad_wrapper
interface as it's way to interface with the pads, see the generic implementation here: https://github.com/lowRISC/opentitan/blob/master/hw/ip/prim_generic/rtl/prim_generic_pad_wrapper.sv
We wouldn't need to support the full capabilities of the wrapper, just use the bits that gives us the same functionality as the pin_X_output_o
/pin_X_output_enable_o
/pin_X_input
interface I proposed above.
We'd then have the pinmux live in sonata_system
wiring the pad wrapper interface out to the top-level. Then have something that looks like the OT padring (https://github.com/lowRISC/opentitan/blob/master/hw/top_earlgrey/rtl/padring.sv) in the FPGA top-level.
What is the use of the output enable and input connected bits? The output enable is inherent in the pin block selection. It could be used for overriding the output enable from a block but are there any use cases for this (when connected to GPIO you get output enable control anyway, when connected to I2C it's vital the I2C block gets control over output enable).
Happy to have the pins each get a dedicated 8 bits with a 5 bit selection field but not sure we've got a compelling use case for the top 3 bits yet so they should all just be reserved.
Ah yes, you're right, the output/input/in_out is specified by the block pins in the YAML description. So lets make bits 7-5
reserved and bits 4-0
selection.
Do we want the ability for an input pin to be fed to multiple blocks?
Do we want the ability for an input pin to be fed to multiple blocks?
I don't think this is required for us, but I also think that our current proposal does allow this.
Some initial thoughts:
I don't think it suffices to think of a pin as being either output or input, because of lines like the SDA line of the I2C bus which is both output and input, and importantly the sda_i
of the I2C block must come from the tri-stated, pull up side of the enabled/disabled output driver, not from 'sda_o' of the I2C block....in this schema is this to be handled by having two pins of the pinmux representing a single SDA line?
I presume USBDEV DP/DN signals are going nowhere near the pinmux because they are potentially even more problematic.
Where the IP blocks are specified, and their input and output pins listed, I think we will need to specify the default state of any input pin when unconnected, to be sure that the IP block can be connected and disconnected without doing anything inappropriate. The prim_onehot_mux that's used at the moment probably wants to support an additional input that is driven out to the IP block input when there is no connection to the external padring.
The pinmux in OT has the nice feature that '0' and '1' are additional configurable inputs, but we can probably get by with static per-input configuration; I'm thinking of, for example, UART and I2C where the lines need to be high by default, to be 'idle.'
I guess we don't need to do anything comparable on pinmux outputs to the FPGA pins; they can just be left as high impedance when no IP block is driving them, but it may be worth checking that can't cause failures of devices on the Sonata board/plug-in boards.
Thanks @alees24, here's my take based on what you said.
When the output is disabled, the pin is set to high-impedance. This is the same behaviour for I2C and GPIO I believe. For the driving of the I2C input when multiple pins are connected to the same I2C host, you need a way of specifying whether to &
or |
the signals (this leads into your last point).
Agreed leaving USB device out of this for now.
For input
and in_out
, I propose we have a dflt
field. For input
this decides what happens when it is disconnected. For in_out
the disconnected state is high impedance, but the default field tells us whether to &
or |
a field if multiple pins are connected to the same IO input. For output
this default is assumed to be high-impedance. Here's how it would look in the block definitions:
- block: uart
ios:
- name: tx
type: output
- name: rx
type: input
dflt: 1
- block: i2c
ios:
- name: scl
type: in_out
dflt: 1
- name: sda
type: in_out
dflt: 1
I also changed pin
to name
in the block IOs to distinguish them from the pins defined in the connection map.
Thanks for clarifying that. I had overlooked the availability of 'in_out' in the example; that helps. Presumably there is a reason that you've shortened 'default' to 'dflt' which I'm afraid I would argue is really not clear/somewhat unreadable? Perhaps we need a clearer alternative.
I thought it looked nice being the same length as name and type, but I agree it is unclear. I will make it default
instead.
I have an alternative proposal for combining in_out
IOs. Each in_out
can specify its combine
policy, which can be no
, and
or or
. no
just means that this IO cannot be combined with others, like GPIO. and
is how I2C IOs should be anded together so that if one device pulls the bus low the whole bus goes low, or
means the opposite where the default state is low and the bus goes high if any of the devices pulls the bus up.
It feels this is just an alternative spelling to push-pull and open-drain?
@alees24 @marnovandermaas please note below.
Here's an outline of a design spec for the pinxmux work. Feel free to suggest different ways to build this, just wanted to get my view of how it would all work written down and thought it'd be useful to have some discussion of the design before we implement it.
The pinmux will allow multiple blocks within Sonata to control a single physical pin. It does not enable dynamic any to any muxing functionality (where any IO from any block can control any pin). Rather a physical pin will have a small number of block IOs it could connect to. E.g. a particular pin could choose between a GPIO, CSn of SPI block 1, RX of UART 1 or SDA of I2C 1. There will be two parts to the pinmux:
Each physical pin will provide the following interface:
Enable programmatic access to pull-up/pull-down configurations could be considered but isn't a critical part of core functionality.
The software interface for the pinmux will consist of a number of registers each filled with copies of an n-bit field (fixed n across all registers). Each field corresponds to a particular physical pin. Writing to that field sets which block controls that pin. The meaning of the index for each field will vary from pin to pin depending on what block IO is available to connect to that physical pin. We may wish to reserve index 0 to mean unconnected (so output enable low and input routed nowhere)
When a pin is connected to a block there are three possible types:
Which type is used is part of the static configuration
Static configuration
The static configuration has two major sections
Block Defintions - Split into two sub-sections, block types and block instances.
A block type provides the name of a block and of the IO that block (giving a name and type for each IO). Block instances specify how many of each block type is available.
An example YAML configuration for the block definition could look like this:
Connection Map - Specifies which block IO a physical pin can choose between
Note that a physical pin can only have a connection to at most one pin in a block. This enables per block indexing for the mux selection field (e.g. say rph_g0 was connected to spi[0].cs_n and spi[0].sck we cannot just have indexes for SPI/UART/RPI GPIO).
With that configuration the pinmux generator will produce RTL that looks something like this:
It does not include the logic that handles the register interface. Note the use of the onehot_mux module with one-hot encoded selector signals. The register interface will need to map writes to the pin selector fields to appropriate updates to these one-hot encoded selector signals. This utlizes more flops but provides the best timing through the muxes. One physical pin field update can need multiple selector signals to be updated. For instance should the pin selector for rph_g0 be written to point to GPIO rather than SPI we need to update the rph_g0_output_sel selector and the rpi_gpio_io_input_sel selector. We get the index 0 disconnect index for free in this setup. When an index of 0 is written the relevant selector signals have 0 written to them which will disable output enable and route the input to nowhere.
Another consideration is what happens when multiple pins are mapped to the same block IO. We could simply say this is a software problem, we'll get odd hardware behaviour as the one-hot muxing will mean the block receives the OR of multiple input pins. Otherwise we could implement detection for this issue in the RTL and disallow the field write but for the first version I'd tend towards saying it's a software problem.
Finally we may want some special case behaviour for I2C. In the current Sonata setup multiple physical pins are wired into the same I2C bus controlled by a single I2C block. We could have a 'I2C chain' concept. And pinmux will allow you to add and remove physical pins from being chained into one I2C block.
We will also want the tool to generate a table that tells us for each physical pin what indexes connect it to what block IO.