Bug 846 - Pinmux Pinspec Interconnect Auto-Generation
Summary: Pinmux Pinspec Interconnect Auto-Generation
Status: CONFIRMED
Alias: None
Product: Libre-SOC's first SoC
Classification: Unclassified
Component: Source Code (show other bugs)
Version: unspecified
Hardware: PC Linux
: --- normal
Assignee: Andrey Miroshnikov
URL:
Depends on: 762
Blocks: 50
  Show dependency treegraph
 
Reported: 2022-06-07 22:22 BST by Andrey Miroshnikov
Modified: 2022-10-01 16:24 BST (History)
2 users (show)

See Also:
NLnet milestone: NLnet.2019.02.012
total budget (EUR) for completion of task and all subtasks: 0
budget (EUR) for this task, excluding subtasks' budget: 0
parent task for budget allocation:
child tasks for budget allocation:
The table of payments (in EUR) for this task; TOML format:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andrey Miroshnikov 2022-06-07 22:22:11 BST
A new bug for the development of the pinmux auto-generation based on given pinspec file. Development in the pinmux repo:
https://git.libre-soc.org/?p=pinmux.git;a=summary
Comment 1 Andrey Miroshnikov 2022-06-07 22:24:09 BST
Copied from #762 c#29:
I've been looking at the functions from pinfunctions.py (sdram1, i2c, etc.).
https://git.libre-soc.org/?p=pinmux.git;a=blob;f=src/spec/pinfunctions.py;h=f1f9558a68005ecbd5dbf2f40f1465d07cd2146e;hb=HEAD#l112

Why does the code in microtest.py (or ls180.py) call these functions with more input arguments than is specified?
https://git.libre-soc.org/?p=pinmux.git;a=blob;f=src/spec/microtest.py;h=ab957b6ae8dc6c596ecf820c6ec3c30d80b449ea;hb=HEAD#l59

For example i2c expects "suffix" and "bank"
def i2s(suffix, bank):
but microtest.py(l#63) does: 
ps.i2c("0", ('A', 1), 2)

Is that a Python-related syntax where you can hide arg parameters?


Also, after running the pinmux_generator.py to make the microtest example, I looked at the output files.
"pinmux.txt" and "microtest.mdwn" show the mux connectivity and are understandable.
However the "litex_pinpads.json" is missing information needed to assign the functions to particular banks.
For the pinspec file from which to generate the dict, am I meant to use the JSON file?
Comment 2 Andrey Miroshnikov 2022-09-17 18:52:03 BST
Previous IRC logs on pinmux (didn't see them being linked anywhere):
https://libre-soc.org/irclog/%23libre-soc.2022-06-01.log.html#t2022-06-01T16:21:37
https://libre-soc.org/irclog/%23libre-soc.2022-06-28.log.html#t2022-06-28T12:22:28
https://libre-soc.org/irclog/%23libre-soc.2022-07-21.log.html#t2022-07-21T17:48:57
https://libre-soc.org/irclog/%23libre-soc.2022-08-02.log.html#t2022-08-02T14:12:34
https://libre-soc.org/irclog/%23libre-soc.2022-09-01.log.html#t2022-09-01T22:53:52

The auto-generated pinmux is working, and you can run the example and view the .gtkw file.
https://git.libre-soc.org/?p=pinmux.git;a=blob;f=src/spec/stage2.py;h=2f79bcff83de1499cd8ab798d3c65da873280e06;hb=cb767260554196bee67d309c67b967ba070fa722

All that's needed for this pinmux to work is a dictionary containing the pads and their mux/bank peripherals (#l404).
The peripheral entry is of the form [name, unit_number, pin] (pin is needed for uart and i2c, as tx/rx, sda/scl have to be defined on separate pads. 

The following dict defines 4 gpios, a uart, and an i2c:
requested = {"N1": {"mux0": ["gpio", 0],
                    "mux1": ["uart", 0, "tx"],
                    "mux2": ["i2c", 0, "sda"]},
             "N2": {"mux0": ["gpio", 1],
                    "mux1": ["uart", 0, "rx"],
                    "mux2": ["i2c", 0, "scl"]},
             "N3": {"mux0": ["gpio", 2]},
             "N4": {"mux0": ["gpio", 3]}
        	}

The disadvantage of this dict is that peripherals are grouped by the pads (thus requiring the user to split the definitions), whereas it's probably more convenient to group by peripheral instead.
For example this format modifies the dummy_pinset by encompassing the extra information for the peripheral within a dict:
{
	'gpio': {'signals':['0*'], 'unit':0, 'pads':['N1'], 'bank':0},
	'uart': {'signals':['tx+', 'rx-'], 'unit':0, 'pads':['N1', 'N2'], 'bank':1},
	'i2c': {'signals':['sda*', 'scl*'], 'unit':0, 'pads':['N1', 'N2'], 'bank':2}
}

I also need to add support for the jtag dir types (+/-/*), because currently those are hard-coded.
I have not extended the gpio entry to support multiple gpio definition at once, but that can be added.

ls180.py/ngi_router.py uses the PinSpec class (base.py) and functions defined in pinfunctions.py (.i2c(), .gpio(), etc.) to create the lists of signals to add. Should I be using these (or extend them to the bank information)?

Should I make these changes? Or do you have other comments/points?
Comment 3 Andrey Miroshnikov 2022-09-21 22:09:21 BST
Added in the IOTypes to the input dict, actually makes the code more flexible :D
https://git.libre-soc.org/?p=pinmux.git;a=commitdiff;h=7b3b0f65fd75922d65f77783fd21680f36ad630a

Now will start looking into PinSpec class usage (inherits Pinouts) and make my mux block us PinSpec instead.
Comment 4 Andrey Miroshnikov 2022-09-21 22:55:58 BST
Added PinSpec into the test code:
https://git.libre-soc.org/?p=pinmux.git;a=commitdiff;h=f658592aa2f3756799aef9fb2b9dfc26092f2513

Will start to integrate it into the pinmux block tomorrow.
Comment 5 Andrey Miroshnikov 2022-10-01 12:33:02 BST
Here's the latest commit:
https://git.libre-soc.org/?p=pinmux.git;a=blob;f=src/stage2.py;h=1669149e22bed1776e7aa7c9ace5772ca0a607f4;hb=HEAD

By reverse engineering the PinSpec "write()" method, I was initially able to
extract only the bank, pin, and mux information.
https://git.libre-soc.org/?p=pinmux.git;a=blob;f=src/spec/base.py;hb=HEAD#l51

ps.pinbanks.keys() <-- Gives the available banks for the chip (A/B/C/D etc)
ps.keys() <-- Gives all pins (summing all banks)

But running in interactive shell was useful to find the rest of the dicts.
Also I answered my own question before finishing this comment.
"keys()" is a method you added to the Pinouts class (which PinSpec inherits)
that internally calls .pins.keys().

ps.byspec <-- dict that has the signal names with IOType information

Now that I have the information, I'll integrate it into the pinmux block by
feeding PinSpec object directly!



There seems to be a strange recursive duplication.
ps.items() gives the pin information (as ps is PinSpec and inherits Pinouts),
but the same dict is returned by ps.gpio.pinouts.items()
and this can be continued for a while (tried up to 20 levels, 4 shown):
ps.gpio.pinouts.gpio.pinouts.gpio.pinouts.gpio.pinouts.items()

Is this a bug? It doesn't seem like there are infinite objects (otherwise my RAM
wouldn't be happy), but perhaps a circular reference somewhere?


Also added new sections to the pinmux doc (they may need to move around, but
haven't focused on that yet), 4.1.1-4.1.4 covering the terminology and the
PinSpec and Pinouts classes.
Comment 6 Luke Kenneth Casson Leighton 2022-10-01 13:33:18 BST
(In reply to Andrey Miroshnikov from comment #5)
> Here's the latest commit:
> https://git.libre-soc.org/?p=pinmux.git;a=blob;f=src/stage2.py;
> h=1669149e22bed1776e7aa7c9ace5772ca0a607f4;hb=HEAD

that's a file, not a commit diff.

  48         self.periph_ports = Array(temp)
  49 

that's extremely unlikely to need Array().  just = temp.

> "keys()" is a method you added to the Pinouts class (which PinSpec inherits)
> that internally calls .pins.keys().

to make it look like the standard python "dict" using Liskov Substitution
Principle, yes.
 
> ps.byspec <-- dict that has the signal names with IOType information
> 
> Now that I have the information, I'll integrate it into the pinmux block by
> feeding PinSpec object directly!

excellent. getting there.

> There seems to be a strange recursive duplication.
> ps.items() gives the pin information (as ps is PinSpec and inherits Pinouts),
> but the same dict is returned by ps.gpio.pinouts.items()

good. one data structure is easily accessible from the other.

> Also added new sections to the pinmux doc (they may need to move around, but
> haven't focused on that yet), 4.1.1-4.1.4 covering the terminology and the
> PinSpec and Pinouts classes.

link?
Comment 7 Andrey Miroshnikov 2022-10-01 16:09:04 BST
(In reply to Luke Kenneth Casson Leighton from comment #6)
> that's a file, not a commit diff.

Ah yes, there:
https://git.libre-soc.org/?p=pinmux.git;a=commitdiff;h=ceaf5478c9ee3b824c00396beb67dc414cb9b2d3

> 
>   48         self.periph_ports = Array(temp)
>   49 
> 
> that's extremely unlikely to need Array().  just = temp.

Without Array() I can't use another signal as the index for setting the mux, this is the error I get:
TypeError: list indices must be integers or slices, not Signal

Unless there's another method, this is the only one I know.


> to make it look like the standard python "dict" using Liskov Substitution
> Principle, yes.

Ah, nice to see an example of this!

> 
> > There seems to be a strange recursive duplication.
> > ps.items() gives the pin information (as ps is PinSpec and inherits Pinouts),
> > but the same dict is returned by ps.gpio.pinouts.items()
> 
> good. one data structure is easily accessible from the other.

By why does it work down to so many levels (presumably 'infinitely', barring memory limits)?
Is there a name for this mechanism (to save you time explaining) so I can read about it at some point? 

> 
> link?

I apologise, forgot to include:
https://libre-soc.org/docs/pinmux/
Comment 8 Luke Kenneth Casson Leighton 2022-10-01 16:24:31 BST
(In reply to Andrey Miroshnikov from comment #7)

> Without Array() I can't use another signal as the index for setting the mux,
> this is the error I get:
> TypeError: list indices must be integers or slices, not Signal

that's a good reason :)

> > good. one data structure is easily accessible from the other.
> 
> By why does it work down to so many levels (presumably 'infinitely', barring
> memory limits)?

same objects.

do you want one object inaccessible when accessing the other?

which one do you not want to be accessible?