Can Someone Please Clearly Explain how to map module outputs to other inputs and outputs using the xbar capability?

ExTelCom

Member
For the life of me I do not understand how to look up which 2-Byte number corresponds to a module's output or input (to use in the XBAR select reigster).

I want to route TMR3_Module2 and also TMR3_Module 1 to one of the AOI modules (to AND them together), and then route that out to any usable pin. I see table 4-5 in section 4.6, and I see QTIMER3_TIMER2 "corresponds" to XBAR1_IN34 but "XBAR1_IN34" in not a 2-Byte number. Is it just the last two characters of the "XBAR1_IN34"? So would the routing simply be: xbar_connect(34, ??AOI input??);?

I then want to route the AOI output to any I/O pin (using the AOI to make the AND of the two input signal from the TIMER3 modules.

See similar entry in that table listing the AOI module's (either inputs OR outputs). Do I have to route TMR3 into XBAR2 first and then tie that output into an XBAR2 input? If I go the other way, I don't see outs of XBAR2 that go to inputs of XBAR3... and that doesn't map neatly onto the concept of figure 60.1 that shows no loop back of xbar3 into xbar2.

Finally (?), I noticed a mention of needing to turn a clock on for the XBAR module because the CPU saves power by defaulting to have all these clocks off. How do I know which one to turn on? ...and how many other "hidden" registers are there burried in this 3522 page document that affect the xbar function or fail?

NXP has crafted an amazing chip here, but the documentation is so confusing (to me) that i think a majority of users are never going to take the chip to its full capabilities. Maybe they need a lot of specific application notes for examples and how to's to fill in where they had to be super terse in the main document.

Thanks in advance!
 
About the AOI modules:
There's 2 of them (AOI1 and AOI2) which map to the XBARB modules (XBAR2 and XBAR3). Each AOI module has 4 outputs, each output has 4 inputs so 16 inputs total. These come from the corresponding XBAR modules, explaining why they have outputs labelled XBAR2_OUT00-15 and XBAR3_OUT00-15. The 4 outputs from each AOI modules feed into the XBAR1 module and have to be mapped to an XBAR1 GPIO output if you want to see them on a pin.

For your case, the first thing to do is get the timer outputs routed to either XBAR2 or XBAR3 to feed it to one of the AOIs. The "xbar_connect" function from Teensy core isn't going to work for this; it's only for configuring XBAR1. You need to read chapter 62 in the reference manual which describes the registers for XBAR2 and XBAR3 - the registers determine which input signals route to the XBAR outputs. As an example, from chapter 4 we know this:
QTIMER3_TIMER1 = XBAR2_IN14
QTIMER3_TIMER2 = XBAR2_IN15
XBAR2_OUT00 = AOI1_IN00
XBAR2_OUT01 = AOI1_IN01
So setting the XBARB2_SEL0 register to (15<<8)|14 will set XBAR2 output #0 to input #14 and output #1 to input #15. These outputs also correspond to AOI1 inputs IN00 (A1) and IN01 (B1).

Now you need to set the product terms for AOI1 event 0, which uses inputs A1/B1/C1/D1 and output AOI1_OUT0. This is done via the AOI1_BFCRT010 and AOI2_BFCRT230 registers. Since you only want to AND the inputs together you only need to use one product term; PT0_AC will be the A input, PT0_BC will be the B input, PT0_CC and PT0_DC will both be logical one, all of the other product terms (PT1_AC -> PT3_DC) will be logic zero.
Ultimately this will configure AOI1_OUT0 to be the product of AO1_IN00 && AO1_IN01.

The final step is getting AOI1_OUT0 mapped to a GPIO pin through XBAR1. This is where you can use xbar_connect(), e.g. xbar_connect(XBARA1_IN_AOI1_OUT0, XBARA1_OUT_IOMUX_XBAR_INOUT04) will map it to XBAR1 output 4. Then you need to set XBAR_INOUT4 to "output" direction (see chapter 11, IOMUXC_GPR_GPR6) and find a gpio that can have its mux set to XBAR1_INOUT4 (IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_02 would be one example).

Yes it's confusing but could easily be simplified if someone made the effort to write generic code for it - there just hasn't really been a need for it because the xbars aren't used very often, and nobody really cares about the AOI modules because they don't have any direct GPIO inputs.
 
Thanks for this. I will try to work through it.

I forgot to mention that I want to keep my TMR3 module 1 & 2 outputs to continue to route to their normal output pins, while _also_ routing to the AOI block to get ANDed together and output as discussed above. What should I know about "copying" those outputs into xbar2 vs. re-routing them through XBAR2?
 
One thing I did was to buy the NXP 1060 EVKB and use the MCUExpresso IDE. You may not elect to do this because you should be able to code anything you want in the Arduino environment. In my case, I wanted to know not just "what" to do (which you can get from code examples), but "why" things are as they are, and I went down a long rabbit hole in pursuit. While I could get some useful code to run on the EVKB, I really wanted to run it on the Teensy 4.1. (There is a way!)

It is much harder to get deep details here because some poor long-suffering sap on this forum has to explain stuff they already know and have moved on from. What I found better than posting noob-ish questions (I got a heap of 'em!!) :) was getting the NXP Reference Manual V3 for the iMXRT1062 and using Claude (Sonnet 5) to help me understand it. Especially the crossbar architecture and the reasons for its existence.

Typical prompt to Claude -" Could you tell me why NXP has such a sophisticated alternate function and cross-bar architecture on the iMXRT1062 compared with the fairly fixed pin mapping approach that exists in the STM32 series of processors?"

These kinds of questions were really helpfully answered, because they did get me acquainted with the philosophical principles and history, not just the declared facts.

Running Teensy from MCUExpresso- I also found a post-link copy function that converts NXP MCUExpresso code (ELF?) to Teensy loader code (HEX), which would load directly to Teensy4.1 using 'Teensyloader.exe'.

// hex file generation in linker post build for Teensy4.1
arm-none-eabi-size "${BuildArtifactFileName}"
arm-none-eabi-objcopy -v -O binary "${BuildArtifactFileName}" "${BuildArtifactFileBaseName}.bin"
arm-none-eabi-objcopy -O ihex "${BuildArtifactFileName}" "${BuildArtifactFileBaseName}.hex"
# checksum -p ${TargetChip} -d "${BuildArtifactFileBaseName}.bin"

That meant I was using the MCUExpresso IDE and Config tool, not the Arduino environment. Once you learn the right steps from the NXP IDE, you could port sections of the code to the Arduino framework and compile it and run it, but that may get a bit top-heavy with all the header files etc. Failing that, you may simply better understand the C++ that has already been written and adapt it for use.

What I found was the need for a proper mental model as I progressed, and this helped significantly.

Useful Link:
There is a mapping of pins to functions available here: that tells you the standard ALT mappings on the iMXRT1062 processor pins and maps them to each Teensy pad.

Short description of my journey:
The processor pins and ALTx mappings are defined in the IOMUXC registers and Daisy-Chain registers (the pads are iMXRT processor pads labelled as "B0_02", "EMC_04", etc). The MCUExpresso 'IOMUXC' macros can get you to the MUX register ALT function on each pin, and the so-called "Daisy-Chain" register, which decides among multiple pin mapping options in each ALTx selector, decides which one you should use, including how to map out to the XBAR to get to an unmapped peripheral (like QDCx). The signal path follows through one of the crossbar registers (probably XBAR1) and then to the unpinned peripheral signal (example QDC1_PHASE_A). Once this idea has gelled, I found that I could use the supplied MCUXpresso macros and CMSIS functions to perform the right configuration with much more confidence. (fsl_iomuxc.h, fsl_enc.h, fsl_enc.c)

So, as you can see, I chose the quadrature encoder (ENC1), also named QDC1, as my experiment. There are 4 quadrature decoders on the iMXRT1062, and none of them has "default" associated pins, such as you may have with LPUART6 or LPSPI4, for example. You have to explicitly map them using the XBAR functions. I had no idea about any of that, and even reading the (3500-page) manual didn't connect the dots for me.

Claude was super useful:
What did connect the dots was engaging Claude and asking for a model of the flows and a description of which signals use which XBAR register selector value, and so, connect the appropriate signal into QDC1. (eg. QDC1_PHASE_A to Teensy pin 4 in my example). I got Claude to check the whole flow path with me and show me how to write the register values to get the correct mappings (kind of bare-metal). Then, the macro preprocessor files bundled with MCUExpresso made sense. I also posted to Claud what I thought was going on - a summary - to test my mental model at each major step. This was the most valuable thing of all in the longer term. Claude could have given me "working code". I could have cut and pasted it, made things "work," learned nothing, and moved on and learned little.

I could even check my code with Claude and ask more in- depth questions. For example, when I used the MCUExpresso-supplied functions, these included configuration of the XBAR direction register. I had not seen the direction register before, and I asked Claude what that was about. The key there is that this register is, by default, set to configure the XBAR signals as all inputs and doesn't have to be touched to make the encoder peripheral work. The code generator, however, included an explicit configuration (good!). This saved a forum question and a "wait for answer" cycle. There were many more examples like this.

After that, I could use the MCUExpresso config tool to generate the code needed, and that made a whole lot more sense. The whole exercise was a rabbit hole, but I contend it was a necessary one. Discovering "why" as well as "how" was far better and freed up a heap of forum time.

Steve
 
Last edited:
Thanks for this. I will try to work through it.

I forgot to mention that I want to keep my TMR3 module 1 & 2 outputs to continue to route to their normal output pins, while _also_ routing to the AOI block to get ANDed together and output as discussed above. What should I know about "copying" those outputs into xbar2 vs. re-routing them through XBAR2?
Unless I've missed something I don't think you need to do anything special for this - module outputs can be routed into xbars without affecting their regular pin outputs.
 
It is much harder to get deep details here because some poor long-suffering sap on this forum has to explain stuff they already know and have moved on from. What I found better than posting noob-ish questions (I got a heap of 'em!!) :) was getting the NXP Reference Manual V3 for the iMXRT1062 and using Claude (Sonnet 5) to help me understand it. Especially the crossbar architecture and the reasons for its existence.
There's a later v4 of the reference manual.
 
Ok - I finally worked through this. Here are some notes I think some other newbie like myself might find useful. Same content also attached as .PDF...


Notes to Orient Uninitiated Users of the XBAR Module:


I find it helps to have a good mental model of what I’m working on before I try to assimilate the documentation for it. I did not have such a model for the XBAR system as I tried to use it. I’m writing this in hopes that this will save other uninitiated users some days of time going through what I did to get things working.

When I refer to “The Manual”, I be referring to the NXP i.XM RT1060 manual that has been annotated for the Teensy 4.1 as of 21-July-2025.
1787098255687.png

The much more helpful figure is figure 60-1 on page 3303.
1787098324717.png


This figure shows the topology of the three crossbars and two AOI modules in a path between two “smaller” crossbar modules and a final “larger” crossbar module.

Now to explain the naming of the registers that route signal into and out of a crossbar module. Despite the text explaining the crossbar select registers, I did not catch on readily. So maybe other users like me can benefit from a brief explanation and diagram or two…

First as most users here probably know, a crossbar network consists of two arrays of signal lines. In the Teensy 4.1 chip, XBAR1 has 88 input row lines and 132output column lines as shown in the following figure. (XBAR 2 and XBAR3 have 64 input lines and 16 outputs each.)

1787098353549.png


A basic crossbar switch network, e.g. XBAR1, with 88 horizontal rows of input lines and 132 vertical output lines.​

At the intersection of each horizontal and vertical line, there is a switch that connects a row input line to a vertical output line. We could address each of those switches by a two-dimensional array with indices (x,y), but for various reasons, that is not efficient in this context. So it is useful to picture the rows of switches as being grouped into horizontal “groups” as shown in the figure on the next page. We can then specify which switch is on by specifying which row we are dealing with and which column we want to designate as ON. (Again this _is_ an X,Y designation , but the row index will sort of go away as I will explain shortly)

Since there are only 132 vertical line choices in XBAR1 (and only 16 in XBAR2 and XBAR3), we can express the switch that is to be ON in each row ‘group” with just a 7 bit number. Since we can fit two such 7-bit numbers into one 16 bit register, NXP has packed two switch row ‘group” values into one register for each such pair. Row 0 and 1 group values are contained in register “XBAR1_SEL0”; the lower half of the register holding the value for input line 0 and the upper half register for holding the value for input line 1. To figure out which register goes with which input rows, divide the row number by 2 (and round down if needed). For example, input lines 8 and 9 are in register XBAR_SEL4.

Finally, you need to know the mapping of what subsystem on the chip feeds into which row line of the XBAR. For XBAR1, that can be found in table 4-5 starting on page 61 (sure, you knew to look in chapter 4 for the line assignments of XBAR in chapter 61 right?) For example, line 0 input to XBAR1 is tied to a logic 0 value (not any particular submodule). This is handy if you want to force a signal stage as you debug your routing… Line 1 is likewise tied to a logic HIGH. If you happen to be using QTIMER3_TIMER2, you’ll find that output signal tied to row 34 of XBAR1.

1787098384948.png



On the output side of XBAR1, you can find a similar table out output column line assignments in table 4-8 starting on page 68 of the manual.

If you want to use the AOI (AND/OR/INVERT) logic blocks, you would be feeding your signals into XBAR2 or XBAR3. The outputs of those AOI blocks then feed into XBAR1 inputs. You can find similar tables of input and output line assignments as tables 4-6. 4-7 (inputs) and 4-9 and 4-10 (outputs).

For XBAR1, there is a function defined (in TeensyDuino PWM.c) to take this low level architecture up one level of abstraction:
xbar_connect(in.out);
where in and out are the defined names (or line numbers) of the signals you want to connect into and out of the XBAR1 switch.

For example, you might write:
xbar_connect(XBARA1_IN_LOGIC_LOW, XBARA1_OUT_IOMUX_XBAR_INOUT06);
which ties a logic LOW constant into XBAR1 and connects it to the line that leads to the pad cell INOUT06.


As another example, you might write:
xbar_connect(XBARA1_IN_AOI1_OUT0, XBARA1_OUT_IOMUX_XBAR_INOUT06);
// take the XBAR1 output to Teensy 4.1 Pin 2 for output. (ALT3 needed for pin 2)
This could also be written as:
xbar_connect(72,6);

Oh, and lest I forget, to make the crossbar module work, you need to turn on clocks that are off by default:
CCM_CCGR2 |= ((uint32_t)(0b1111<<22)); // CLOCKS ON for xbar 1 and 2 CCGR2[CG11] and CCGR2[CG12]
// CCM_CCGR2 |= ((uint32_t)(0b11<<14)); // CLOCKS ON for xbar 3 CCGR2[CG7] pp. 1077 - 1070 (if ever needed.)
// CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); // e.g. as done in other spaces...


I hope this helps some other people skip over the days it took me reach critical mass on this topic.
Many thanks to those forum folks above whose inputs got me there.

Ex-
 

Attachments

  • XBAR Introduction and Explanation.pdf
    215.5 KB · Views: 32
...and here is the actual code for the crossbar and AOI portion of my "exercise" ...

//*************************************** 2nd part: crossbar *******************************
// Now to route TIMERs 1 and 2 through xbar2 -> AOI1 -> xbar1 -> output cell -> output pin..
// ... to combine CS AND SCLK to supress the SCLKs "outside" of CS.
// QTIMER3_TIMER1 = XBAR2_IN13 QTIMER3_TIMER1 is bus line 13 of XBAR2 see p. 64
// QTIMER3_TIMER2 = XBAR2_IN14 QTIMER3_TIMER2 is bus line 14 of XBAR2 see p. 64
// AOI1 A B C D ... A will be QTIMER3_TIMER1, B will be QTIMER3_TIMER2,
// XBAR2_OUT00 = AOI1_IN00 XBAR2_OUT00 is chip-wired bus line into AOI1_IN00 see p. 72
// XBAR2_OUT01 = AOI1_IN01
// So XBAR2 IN 13 needs to connect to XBAR2 OUT 00 and likewise 14 to 01


CCM_CCGR2 |= ((uint32_t)(0b1111<<22)); // CLOCKS ON for xbar 1 and 2 CCGR2[CG11] and CCGR2[CG12]
// CCM_CCGR2 |= ((uint32_t)(0b11<<14)); // CLOCKS ON for xbar 3 CCGR2[CG7] pp. 1077 - 1070 (if ever needed.)
// CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); // e.g. as done in other spaces...

// pinMode(2, OUTPUT); // pin 2 use for XBARA1 output (this line not really needed)
IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_04 = 0;
IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_04 |= ((uint32_t)(0b011 << 0)); // ALT3 to select pin 2 as XBAR1_INOUT06 p. 429

IOMUXC_GPR_GPR6 |= ((uint32_t)(1<<18)); // Set bit for XBAR_INOUT06 as output. p.342

XBARB2_SEL0 = 0;
XBARB2_SEL0 = ((uint16_t)(14<<8 | 13)); // QTMR3 2 and 1 into XBAR2 14 & 13

// AOI configuration to AND the two inputs
AOI1_BFCRT010 = ((uint16_t)(0b1001111100000000)); // configure the AOI logic: Invert A, Pass B, set C & D HIGH. p. 3367
//AOI1_BFCRT010 = ((uint16_t)(0xFF)); // configure the AOI logic for hard on for debug
//AOI1_BFCRT230 = ((uint16_t)(0xFF));

// AOI1_OUT0 is wired on-chip into XBAR1_IN72. So we don't need to define that path. see p. 63

// for XBAR1 output, we need to take the output from an XBAR1 line that is linkable to pin 2. This is hunt and peck.
// looking at page 315, we find for XBAR1, I can use Teensy 4.1 pin 2, which is native GPIO_EMC_04, ...
// ... and EMC_04 is fed by XBAR_INOUT06. So xbar1's in line 72 is going to go to xbar1's out line 6
// three ways to write it for Teensy 4.1:
// xbar_connect(XBARA1_IN_AOI1_OUT0, XBARA1_OUT_IOMUX_XBAR_INOUT06); // take the XBAR1 output to Teensy 4.1 Pin 2 for output
// xbar_connect(72,6); // shorter version: xbar-connect is only valid for XBAR1
XBARA1_SEL3 |= ((uint16_t)(72)); // the low 7 bits of XBARA1_SEL3 hold the input line number for output line XBARA_OUT6
}

void loop()
{
delay(10);
// AOI1_BFCRT010 = ((uint16_t)(0xFFFF)); // debug: configure the AOI logic to drive outut HIGH regardless
// AOI1_BFCRT230 = ((uint16_t)(0xFFFF)); // .. from the "front end" of the AOI block.
// xbar_connect(XBARA1_IN_LOGIC_HIGH, XBARA1_OUT_IOMUX_XBAR_INOUT06); // another way to force AOI output HIGH, skip the logic
// XBARA1_SEL3 = 1; // yet another way to force a HIGH w/o logic

//delay(10);
// AOI1_BFCRT010 = ((uint16_t)(0x00)); // debug: configure the AOI logic to drive output LOW regardless
// AOI1_BFCRT230 = ((uint16_t)(0x00));
// xbar_connect(XBARA1_IN_LOGIC_LOW, XBARA1_OUT_IOMUX_XBAR_INOUT06);
// XBARA1_SEL3 = 0;

}
 
...and here is the actual code for the crossbar and AOI portion of my "exercise" ...

//*************************************** 2nd part: crossbar *******************************
// Now to route TIMERs 1 and 2 through xbar2 -> AOI1 -> xbar1 -> output cell -> output pin..
// ... to combine CS AND SCLK to supress the SCLKs "outside" of CS.
// QTIMER3_TIMER1 = XBAR2_IN13 QTIMER3_TIMER1 is bus line 13 of XBAR2 see p. 64
// QTIMER3_TIMER2 = XBAR2_IN14 QTIMER3_TIMER2 is bus line 14 of XBAR2 see p. 64
// AOI1 A B C D ... A will be QTIMER3_TIMER1, B will be QTIMER3_TIMER2,
// XBAR2_OUT00 = AOI1_IN00 XBAR2_OUT00 is chip-wired bus line into AOI1_IN00 see p. 72
// XBAR2_OUT01 = AOI1_IN01
// So XBAR2 IN 13 needs to connect to XBAR2 OUT 00 and likewise 14 to 01


CCM_CCGR2 |= ((uint32_t)(0b1111<<22)); // CLOCKS ON for xbar 1 and 2 CCGR2[CG11] and CCGR2[CG12]
// CCM_CCGR2 |= ((uint32_t)(0b11<<14)); // CLOCKS ON for xbar 3 CCGR2[CG7] pp. 1077 - 1070 (if ever needed.)
// CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); // e.g. as done in other spaces...

// pinMode(2, OUTPUT); // pin 2 use for XBARA1 output (this line not really needed)
IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_04 = 0;
IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_04 |= ((uint32_t)(0b011 << 0)); // ALT3 to select pin 2 as XBAR1_INOUT06 p. 429

IOMUXC_GPR_GPR6 |= ((uint32_t)(1<<18)); // Set bit for XBAR_INOUT06 as output. p.342

XBARB2_SEL0 = 0;
XBARB2_SEL0 = ((uint16_t)(14<<8 | 13)); // QTMR3 2 and 1 into XBAR2 14 & 13

// AOI configuration to AND the two inputs
AOI1_BFCRT010 = ((uint16_t)(0b1001111100000000)); // configure the AOI logic: Invert A, Pass B, set C & D HIGH. p. 3367
//AOI1_BFCRT010 = ((uint16_t)(0xFF)); // configure the AOI logic for hard on for debug
//AOI1_BFCRT230 = ((uint16_t)(0xFF));

// AOI1_OUT0 is wired on-chip into XBAR1_IN72. So we don't need to define that path. see p. 63

// for XBAR1 output, we need to take the output from an XBAR1 line that is linkable to pin 2. This is hunt and peck.
// looking at page 315, we find for XBAR1, I can use Teensy 4.1 pin 2, which is native GPIO_EMC_04, ...
// ... and EMC_04 is fed by XBAR_INOUT06. So xbar1's in line 72 is going to go to xbar1's out line 6
// three ways to write it for Teensy 4.1:
// xbar_connect(XBARA1_IN_AOI1_OUT0, XBARA1_OUT_IOMUX_XBAR_INOUT06); // take the XBAR1 output to Teensy 4.1 Pin 2 for output
// xbar_connect(72,6); // shorter version: xbar-connect is only valid for XBAR1
XBARA1_SEL3 |= ((uint16_t)(72)); // the low 7 bits of XBARA1_SEL3 hold the input line number for output line XBARA_OUT6
}

void loop()
{
delay(10);
// AOI1_BFCRT010 = ((uint16_t)(0xFFFF)); // debug: configure the AOI logic to drive outut HIGH regardless
// AOI1_BFCRT230 = ((uint16_t)(0xFFFF)); // .. from the "front end" of the AOI block.
// xbar_connect(XBARA1_IN_LOGIC_HIGH, XBARA1_OUT_IOMUX_XBAR_INOUT06); // another way to force AOI output HIGH, skip the logic
// XBARA1_SEL3 = 1; // yet another way to force a HIGH w/o logic

//delay(10);
// AOI1_BFCRT010 = ((uint16_t)(0x00)); // debug: configure the AOI logic to drive output LOW regardless
// AOI1_BFCRT230 = ((uint16_t)(0x00));
// xbar_connect(XBARA1_IN_LOGIC_LOW, XBARA1_OUT_IOMUX_XBAR_INOUT06);
// XBARA1_SEL3 = 0;

}
These posts are probably the best rendition of the XBAR architecture I have seen. It belongs on a link accessible from here. I could find nothing like this online. I solved my particular problem for the encoder module, but this shows the whole landscape in a way that is exhaustive, logical and uncomplicated. Nice text context for the diagrams. Really good stuff. As you say, it's about the mental model first and then about 'how' to get the right result. This is excellent work.
Steve
 
The description and diagram explaining the XBAR_SEL registers are backwards: the registers correspond to output lines (2 per 32-bit register) while the values in them determine which inputs are used. So for example:
Row 0 and 1 group values are contained in register “XBAR1_SEL0”; the lower half of the register holding the value for input line 0 and the upper half register for holding the value for input line 1. To figure out which register goes with which input rows, divide the row number by 2 (and round down if needed). For example, input lines 8 and 9 are in register XBAR_SEL4.
Every occurrence of "input" in this text should be "output".
 
Dang! I believe jmarsh is correct! I was afraid I would get that wrong.
I should have noticed that there are 66 XBAR1_SELnn registers. With each one holding two connections, that covers the 132 output lines, not the 88 input lines.

There is also the clue in the code for the xbar_connect() routine:
void xbar_connect(unsigned int input, unsigned int output)
{
if (input >= 88) return;
if (output >= 132) return;
#if 1
volatile uint16_t *xbar = &XBARA1_SEL0 + (output / 2);
...


The register to put the connect value into is selected by dividing the desired output line by 2. Again, this means there are 132 columns of muxes not 88 rows of muxes.
I will re-do the drawings and correct this error.... My bad. Sorry.

(Is it better practice for me to edit my post with the errors above and re-write the text and replace the bad image in that, or is it considered proper to leave that up and provide revised info below? Please advise. thanks.)
 
Last edited:
The following corrects what I got backwards in the post above. This clarifies that the XBAR MUX's are arranged in columns not rows.

Notes to Orient Uninitiated Users of the XBAR Module:

I find it helps to have a good mental model of what I’m working on before I try to assimilate the documentation for it. I did not have such a model for the XBAR system as I tried to use it. I’m writing this in hopes that this will save other uninitiated users some days of time going through what I did to get things working.

When I refer to “The Manual”, I be referring to the NXP i.XM RT1060 manual that has been annotated for the Teensy 4.1 as of 21-July-2025.

First of all. There is a figure in the Manual (63-2) which I found misleading. Topologically, it might be correct. However, the layout implies some retrograde path construct involved in getting a signal out of the AOI and back into another (or even the same) crossbar module. Again, topologically, the figure might be correct, but there is not special operation needed to route into and out of the AOI module, and I found this figure confusing.

1787251902471.png
The much more helpful figure is figure 60-1 on page 3303.
1787251869193.png

This figure shows the topology of the three crossbars and two AOI modules in a path between two “smaller” crossbar modules and a final “larger” crossbar module.

Now to explain the naming of the registers that route signal into and out of a crossbar module. Despite the text explaining the crossbar select registers, I did not catch on readily. So maybe other users like me can benefit from a brief explanation and diagram or two…

First as most users here probably know, a crossbar network consists of two arrays of signal lines. In the Teensy 4.1 chip, XBAR1 has 88 input row lines and 132 output column lines as shown in the following figure. (XBAR 2 and XBAR3 have 64 input lines and 16 outputs each.)

1787251839292.png

A basic crossbar switch network, e.g. XBAR1, with 88 horizontal rows of input lines and 132 vertical output lines.

At the intersection of each horizontal and vertical line, there is a switch that connects a row input line to a vertical output line. We could address each of those switches by a two-dimensional array with indices (x,y), but for various reasons, that is not efficient in this context. So it is useful to picture the columns of switches as being grouped into vertical “groups” as shown in the figure on the next page. We can then specify which switch is on by specifying which column we are dealing with and which row we want to designate as connecting onto that column. (Again this _is_ an X,Y designation , but the explicit column index will sort of go away as I will explain shortly.) In proper E.E. parlance, these “groups” are MUX’s with 88 input lines and one output feeding one column line of the crossbar.

1787250671237.png


Since there are only 88 input line choices in XBAR1 (and only 64 in XBAR2 and XBAR3), we can express the switch that is to be ON in each column “group” (MUX) with just a 7 bit number. Since we can fit two such 7-bit numbers into one 16 bit register, NXP has packed two MUX-select values into one register for each two columns. Column 0 and 1 MUX values are contained in register “XBAR1_SEL0”; the lower half of the register holding the value for column 0 and the upper half register for holding the value for column 1. To figure out which register goes with which output column, divide the column number by 2 (and round down if needed). For example, the switch value for output columns 8 and 9 are in register XBAR_SEL4.

Finally, you need to know the mapping of what subsystem on the chip feeds into which row line of the XBAR. For XBAR1, that can be found in table 4-5 starting on page 61 (sure, you knew to look in chapter 4 for the line assignments of XBAR in chapter 61 right?) For example, line 0 input to XBAR1 is tied to a logic 0 value (not any particular submodule). This is handy if you want to force a signal stage as you debug your routing… Line 1 is likewise tied to a logic HIGH. If you happen to be using QTIMER3_TIMER2, you’ll find that output signal tied to row 34 of XBAR1.

On the output side of XBAR1, you can find a similar table out output column line assignments in table 4-8 starting on page 68 of the manual.

If you want to use the AOI (AND/OR/INVERT) logic blocks, you would be feeding your signals into XBAR2 or XBAR3. The outputs of those AOI blocks then feed into XBAR1 inputs. You can find similar tables of input and output line assignments as tables 4-6. 4-7 (inputs) and 4-9 and 4-10 (outputs).

For XBAR1, there is a function defined (in TeensyDuino PWM.c) to take this low level architecture up one level of abstraction:
xbar_connect(in.out);
where “in” and “out” are the defined names (or line numbers) of the signals you want to connect into and out of the XBAR1 switch.

For example, you might write:
xbar_connect(XBARA1_IN_LOGIC_LOW, XBARA1_OUT_IOMUX_XBAR_INOUT06);
which ties a logic LOW constant into XBAR1 and connects it to the line that leads to the pad cell INOUT06.

As another example, you might write:
xbar_connect(XBARA1_IN_AOI1_OUT0, XBARA1_OUT_IOMUX_XBAR_INOUT06);
// take the XBAR1 output to Teensy 4.1 Pin 2 for output. (ALT3 needed for pin 2)
This could also be written as:
xbar_connect(72,6);

Oh, and lest I forget, to make the crossbar module work, you need to turn on clocks that are off by default. There are three separate clocks, one for each module:

CCM_CCGR2 |= ((uint32_t)(0b1111<<22)); // CLOCKS ON for xbar 1 and 2 CCGR2[CG11] and CCGR2[CG12]
// CCM_CCGR2 |= ((uint32_t)(0b11<<14)); // CLOCKS ON for xbar 3 CCGR2[CG7] pp. 1077 - 1070 (if ever needed.)
// CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); // e.g. as done in other spaces...

Finally, because the XBAR system can feed signals both "outwards" and "inwards", you need to designate whether your signal is going "out" or "in" using register IOMUXC_GPR_GPR6:
IOMUXC_GPR_GPR6 |= ((uint32_t)(1<<18)); // Set bit for XBAR_INOUT06 as output. p.342

I hope this helps some other people skip over the days it took me reach critical mass on this topic.
Many thanks to those forum folks above whose inputs got me there.
 

Attachments

  • XBAR Introduction and Explanation Corrected c.pdf
    267.2 KB · Views: 39
Some hints with using these registers with the imxrt1060-regs library. Note that there's sometimes more than one way of doing something.
  1. Two clocks at once:
    CCM::group->CCGR2 |= CCM::CCGR2::XBAR2(CCM::CCGR::kOn) | CCM::XBAR1(CCM::CCGR::kOn);
  2. Individual clocks:
    CCM::CCGR2::XBAR2 = CCM::CCGR::kOn; and
    CCM::CCGR2::XBAR1 = CCM::CCGR::kOn;
  3. Direction:
    IOMUXC_GPR::GPR6::IOMUXC_XBAR_DIR_SEL_6 = 1; // XBAR_INOUT06 as output
  4. One possible link:
    XBARA1::SEL3::SEL6 = XBARA1::IN::kLOGIC_LOW;
  5. Another possible link:
    XBARA1::SEL3::SEL6 = XBARA1::IN::kAOI1_OUT0;
  6. Or even:
    XBARA1::connect(XBARA1::IN::kLOGIC_LOW, XBARA1::OUT::kIOMUX_XBAR_INOUT06); and
    XBARA1::connect(XBARA1::IN::kAOI1_OUT0, XBARA1::OUT::kIOMUX_XBAR_INOUT06);
It looks a teensy bit long winded, however, there are no masks or shifts to remember, and no macro functions to pass accidentally-bad constants to.
 
Last edited:
Something that really bugs me about Figure 60-1 from the reference manual: it shows IO Pads being input to XBAR2 and XBAR3 but that isn't the case, only other internal modules can be used as inputs to the AOI products. If you want to use IOs (pins) as general logic function inputs the only real option is FlexIO and it's very restricted on which pins can be used.
 
Is it possible to route an input signal through XBAR1 to an unused input of an unused module (e.g. a trigger or secondary input of a quadtimer) and then use that line as an input to XBAR2 or XBAR3 for the logic function? It'd be a bit of a waste of resources to give up say a Qtimer to do a logic function, but if it was to go unused anyway, then it wouldn't be a loss. I don't know if that's possible; I haven't explored the XBAR in and out tables enough to see if that is possible... (By the way, does anyone know what the little bubble with the "S" in it means in figure 60-1?)
 
Back
Top