[libre-riscv-dev] pinmux to generate interfaces (with no pinmux)

Luke Kenneth Casson Leighton lkcl at lkcl.net
Sat May 9 10:59:13 BST 2020


transferring this to the list

https://git.libre-soc.org/?p=pinmux.git


On Sat, May 9, 2020 at 9:41 AM Luke Kenneth Casson Leighton
<lkcl at lkcl.net> wrote:
>
> $ python ./src/pinmux_generator.py -s i_class -v -o i_class
> $ python ./src/pinmux_generator.py  -v -o i_class
>
> then ignore the bsv_src and examine i_class.mdwn.

same for minitest and microtest.

note: for EINT, GPIO and PWM because its naming is a different
convention (auto-repeated numbering of a single function - PWM0, PWM1,
PWM2) you do *not* specify the 1st argument.

   ps.gpio("", ('A', 1), 0, 0, 3)

NOT  ps.gpio("A", ('A', 1), 0, 0, 3)
or    ps.gpio("0", ('A', 1), 0, 0, 3)

this means "add GPIOA" starting from position 1, starting from
numbering 0 (GPIOA_A0..), and add 3 of them.  result:

## Bank A (3 pins, width 4)

| Pin | Mux0        | Mux1        | Mux2        | Mux3        |
| --- | ----------- | ----------- | ----------- | ----------- |
|   0 |             | A UART0_TX  |             |             |
|   1 | A GPIOA_A0  | A UART0_RX  | A TWI0_SDA  |             |
|   2 | A GPIOA_A1  | A UART1_TX  | A TWI0_SCL  |             |
|   3 | A GPIOA_A2  | A UART1_RX  |             | A TWI0_SDA  |



ps.gpio("", ('A', 0), 0, 1, 3)

this means "start numbering from 1" (GPIOA_A1/2/3)

result:

## Bank A (3 pins, width 4)

| Pin | Mux0        | Mux1        | Mux2        | Mux3        |
| --- | ----------- | ----------- | ----------- | ----------- |
|   0 | A GPIOA_A1  | A UART0_TX  |             |             |
|   1 | A GPIOA_A2  | A UART0_RX  | A TWI0_SDA  |             |
|   2 | A GPIOA_A3  | A UART1_TX  | A TWI0_SCL  |             |
|   3 |             | A UART1_RX  |             |             |


unfortunately you can't place GPIO pins in Mux1, Mux2 or Mux3 columns
(i just tried) - this is undesirable anyway because you *want* GPIO to
be in the Mux0 column.


however you _can_ place uart, i2c etc. in Mux0:

    ps.uart("0", ('A', 0), 0)   <=== Mux0 column
    ps.uart("1", ('A', 2), 1)   <=== Mux1 column

result:

## Bank A (3 pins, width 4)

| Pin | Mux0        | Mux1        | Mux2        | Mux3        |
| --- | ----------- | ----------- | ----------- | ----------- |
|   0 | A UART0_TX  |             |             |             |
|   1 | A UART0_RX  |             | A TWI0_SDA  |             |
|   2 |             | A UART1_TX  | A TWI0_SCL  |             |
|   3 |             | A UART1_RX  |             | A TWI0_SDA  |


so we will want something like:

def pinspec():
    pinbanks = {
        'A': (10, 1), <<<--- only one column (no pinmux, effectively)
    }
   ....
   ....
    ps.gpio("", ('A', 0), 0, 0, 3)  # 3 pins, bank A row 0+1+2, starting in col0
    ps.uart("0", ('A', 4), 0)        # UART0 in bank A row 4+5, col0
    ps.uart("1", ('A', 6), 0)        # UART1 in bank A row 6+7, col0
    ps.i2c("0", ('A', 8), 0)         # TWI0 in bank A row 8+9, col0

which will generate this:

## Bank A (10 pins, width 1)

| Pin | Mux0        | Mux1        | Mux2        | Mux3        |
| --- | ----------- | ----------- | ----------- | ----------- |
|   0 | A GPIOA_A0  |
|   1 | A GPIOA_A1  |
|   2 | A GPIOA_A2  |
|   4 | A UART0_TX  |
|   5 | A UART0_RX  |
|   6 | A UART1_TX  |
|   7 | A UART1_RX  |
|   8 | A TWI0_SDA  |
|   9 | A TWI0_SCL  |

ignore the extra headings they are just a "print" statement, hard-coded.


test scenarios are specified with ps.add_scenario().  for each
"customer scenario" add what interfaces are desired by the customer,
and the add_scenario() function will *automatically go hunting for
that interface*.

in a multiplexed design this allows you to make damn sure that *ALL*
scenarios are covered by the chip.  design something that's not going
to cover a potential (or actual) customer's requirements and you just
wasted USD $20 million.

the usefulness of the add_scenario() function in our case is the
auto-generation of the documentation (markdown file) which "describes"
the customer scenario in its own section.


there's plenty of examples (c_class, i_class, minitest, microtest) -
just bear in mind *move slowly and incrementally*.  add just the one
function at a time, or start straight away from minitest or something.

btw *sigh* you'll need to add the new spec actually to src/spec/__init__.py:
from spec import m_class
from spec import c_class
from spec import i_class
from spec import minitest
from spec import microtest
from spec.gen import specgen
from spec.testing import dummytest

modules = {'m_class': m_class,
           'c_class': c_class,
           'i_class': i_class,
           'minitest': minitest,
           'microtest': microtest
           }


adding a new pinfunction is in src/spec/pinfunctions.py:

note that each interface function returns *two* lists:

* list of all pins (including those below)
* list of pins that are part of a bi-directional bus under the control
of a "direction" pin

note therefore that ulpi() has a bug :)

def ulpi(suffix, bank):
    ulpipins = ['CK+', 'DIR+', 'STP+', 'NXT+']
    for i in range(8):
        ulpipins.append('D%d*' % i)
    return (ulpipins, [])

should be similar to nspi and emmc.


basically, to add LPC as an interface, cookie-cut cut/paste emmc or nspi.

oh - it uses python2, and it's fine that way (there's nothing special
about it), so please don't spend time converting it to python3, it'll
take too long.

l.



More information about the libre-riscv-dev mailing list