Hi,
I would like to configure l2 lookup table dynamically how can I configure it ?
Without using ECT tool how can I configure the l2 lookup table ?
Any examples?
Regards
Atkinson
Hi @PavelL
"In your code, there are missing shift values in entry[3], and missing or misplaced brackets in entry[3], entry[4], and entry[5]."
I have implemented it as same as the python code sorry i am nots sure where i am missing the brackets and shift values. can you kindly provide me the aligned code which might be more clear sorry for missing out the brackets and shift
It would be more helpful and clear if you provide me the exact entry[3],entry[4], entry[5]
Hello @Atkinson ,
In your code, there are missing shift values in entry[3], and missing or misplaced brackets in entry[3], entry[4], and entry[5].
To verify your L2 lookup configuration, you may consider setting the entry as a static one instead of dynamic — this can help isolate whether the issue is with the entry format or the dynamic update mechanism.
Also, please note that the "pretty tables" in the Python script are used only for formatting output in the command window — they do not affect the functionality or register configuration.
Best regards,
Pavel
Hi @PavelL
int WriteL2LookupEntry(int cs)
{
const uint32_t DESTPORTS = 0x004;
const uint32_t INDEX = 2;
const uint64_t MACADDR = 0x112233445566;
const uint64_t MACADDR_MASK = 0xffffffffffff;
const uint32_t VLANID = 0;
const uint32_t VLANID_MASK = 0x0;
const uint32_t IOTAG = 0;
const uint32_t IOTAG_MASK = 0;
const uint32_t SRCPORT = 0x6;
const uint32_t SRCPORT_MASK = 0xF;
const uint32_t ENFPORT = 0;
const uint32_t RETAG = 0;
const uint32_t MIRROR = 0;
const uint32_t TAKETS = 0;
const uint32_t MIRR_VLAN = 0xFFF;
const uint32_t TRAPTOHOST = 0;
const uint32_t CBSIDIN = 0x3F;
const uint32_t CBENONDA = 0;
const uint32_t VLIDX = 0xFFF;
const uint32_t SA_POLICE = 0;
const uint32_t DA_POLICE = 0;
uint32_t L2_OFFSET = 0x23;
uint32_t L2CTRL_OFFSET = 0x29;
const uint32_t SWREG_BASE = 0x00000000;
const uint32_t VALID = 1U;
const uint32_t RDWRSET = 1U;
const uint32_t VALIDENT = 1U;
const uint32_t HOSTCMD = 3U;
const uint32_t MGMTROUTE = 0U;
const uint32_t LOCKED = 1U;
const uint32_t ERROR = 0U;
uint32_t L2_ADDRESS = SWREG_BASE + L2_OFFSET;
uint32_t L2_ADDRESS_CTL = SWREG_BASE + L2CTRL_OFFSET;
uint32_t l2Entry[ADDRESS_RANGE] = {0};
l2Entry[0] = (((INDEX & 0x03ff) << (1 % 32)) + ((ENFPORT & 1) << (11 % 32))
+ ((DESTPORTS & 0x7FF) << (12 % 32)) + ((SRCPORT & 0xF) << (23 % 32))
+ ((MACADDR & 0x1F) << (27 % 32)));
l2Entry[1] = (((MACADDR >> 5) & 0xFFFFFFFF));
l2Entry[2] = ((((MACADDR >> 37) & 0x7FF) << (64 % 32)) + ((VLANID & 0xFFF) << (75 % 32))
+ ((IOTAG & 1) << (87 % 32)) + ((MACADDR_MASK & 0xFF) << (88 % 32)));
l2Entry[3] = (((MACADDR_MASK >>
l2Entry[4] = (((MACADDR_MASK >> 40) & 0xFF << (128 % 32)) + ((VLANID_MASK & 0xfff) << (136 % 32))
+ ((IOTAG_MASK & 1) << (148 % 32)) + ((SRCPORT_MASK & 0xF) << (149 % 32))
+ ((RETAG & 0x1) << (153 % 32)) + ((MIRROR & 0x1) << (154 % 32))
+ ((TAKETS & 0x1) << (155 % 32)) + ((MIRR_VLAN & 0xF) << (156 % 32)));
l2Entry[5] = (((MIRR_VLAN >> 4) & 0xFF << (160 % 32)) + ((TRAPTOHOST & 0x1) << (168 % 32))
+ ((CBSIDIN & 0x3F) << (169 % 32)) + ((CBENONDA & 0x1) << (175 % 32))
+ ((VLIDX & 0xFFF) << (177 % 32)) + ((SA_POLICE & 0x1) << (189 % 32))
+ ((DA_POLICE & 0x1) << (190 % 32)));
uint32_t status = 0;
do {
Std_ReturnType ret = spi_read_reg(L2_ADDRESS_CTL, &status);
if (ret != E_OK) {
return -1;
}
} while ((status >> VALID_BIT_SHIFT) & 1U);
for (int i = 0; i < ADDRESS_RANGE; i++) {
Std_ReturnType ret = spi_write_reg(L2_ADDRESS + i, l2Entry[i]);
if (ret != E_OK) {
return -2;
}
uint32_t readBack = 0;
ret = spi_read_reg(L2_ADDRESS + i, &readBack);
if (ret != E_OK || readBack != l2Entry[i]) {
return -3;
}
}
uint32_t controlL2Entry = 0;
controlL2Entry = ((VALID << 31) + (RDWRSET << 30) + (ERROR << 29) + (LOCKED << 28) + (VALIDENT << 27) +
(MGMTROUTE << 26) + (HOSTCMD << 23));
Std_ReturnType ret = spi_write_reg(L2_ADDRESS_CTL, controlL2Entry);
if (ret != E_OK) {
return -4;
}
do {
ret = spi_read_reg(L2_ADDRESS_CTL, &status);
if (ret != E_OK) {
return -5;
}
} while ((status >> VALID_BIT_SHIFT) & 1U);
return 0;
}
I have changed the implementation similar to Python script. if you found any errors can you send me the changes please
Python script
"After writing, try sending a frame that matches the MAC/VLAN/port criteria of the entry and observe whether it is forwarded according to the DESTPORTS field"
I am trying to send in this way as you mentioned but this reflection is not seen
I am unable to see my entry is getting reflected ?
is there any other things need to be setted up ??
In Python script after writing the register value for controll2entry into L2_ADDRESS_CTL
there were few lines of code which does adding a entry into "prettytable"
Do we need to this also same and whether we have provision to do this ??
Hello @Atkinson ,
There are missing shift values and incomplete expressions in entry[3], entry[4], and entry[5]. These may result in incorrect register values. I recommend comparing your logic against the Python reference script, which constructs the L2 entry correctly.
After writing, try sending a frame that matches the MAC/VLAN/port criteria of the entry and observe whether it is forwarded according to the DESTPORTS field.
Best regards,
Pavel
Hi @PavelL
int WriteL2LookupEntry(int cs)
{
const uint32_t DESTPORTS = 0x002;
const uint32_t INDEX = 8;
const uint64_t MACADDR = 0x661122334455;
const uint64_t MACADDR_MASK = 0xffffffffffff;
const uint32_t VLANID = 0;
const uint32_t VLANID_MASK = 0xfff;
const uint32_t IOTAG = 0;
const uint32_t IOTAG_MASK = 0;
const uint32_t SRCPORT = 0x5;
const uint32_t SRCPORT_MASK = 0xF;
const uint32_t ENFPORT = 0;
const uint32_t RETAG = 0;
const uint32_t MIRROR = 0;
const uint32_t TAKETS = 0;
const uint32_t MIRR_VLAN = 0xFFF;
const uint32_t TRAPTOHOST = 0;
const uint32_t CBSIDIN = 0x3F;
const uint32_t CBENONDA = 0;
const uint32_t VLIDX = 0xFFF;
const uint32_t SA_POLICE = 0;
const uint32_t DA_POLICE = 0;
uint32_t L2_OFFSET = 0x23;
uint32_t L2CTRL_OFFSET = 0x29;
const uint32_t SWREG_BASE = 0x00000000;
const uint32_t VALID = 1U;
const uint32_t RDWRSET = 1U;
const uint32_t VALIDENT = 1U;
const uint32_t HOSTCMD = 3U;
const uint32_t MGMTROUTE = 0U;
const uint32_t LOCKED = 1U;
const uint32_t ERROR = 0U;
uint32_t L2_ADDRESS = SWREG_BASE + L2_OFFSET;
uint32_t L2_ADDRESS_CTL = SWREG_BASE + L2CTRL_OFFSET;
uint32_t l2Entry[ADDRESS_RANGE] = {0};
l2Entry[0] = ((INDEX & 0x03FF) << 1)
| ((ENFPORT & 0x1) << 11)
| ((DESTPORTS & 0x7FF) << 12)
| ((SRCPORT & 0xF) << 23)
| ((uint32_t)(MACADDR & 0x1F) << 27);
l2Entry[1] = (uint32_t)((MACADDR >> 5) & 0xFFFFFFFF);
l2Entry[2] = (uint32_t)(((MACADDR >> 37) & 0x7FF) << 0)
| ((VLANID & 0xFFF) << 11)
| ((IOTAG & 0x1) << 23)
| ((uint32_t)(MACADDR_MASK & 0xFF) << 24);
l2Entry[3] = (uint32_t)((MACADDR_MASK >>
l2Entry[4] = ((uint32_t)((MACADDR_MASK >> 40) & 0xFF) << 0)
| ((VLANID_MASK & 0xFFF) <<
| ((IOTAG_MASK & 0x1) << 20)
| ((SRCPORT_MASK & 0xF) << 21)
| ((RETAG & 0x1) << 25)
| ((MIRROR & 0x1) << 26)
| ((TAKETS & 0x1) << 27)
| ((MIRR_VLAN & 0xF) << 28);
l2Entry[5] = (((MIRR_VLAN >> 4) & 0xFF) << 0)
| ((TRAPTOHOST & 0x1) <<
| ((CBSIDIN & 0x3F) << 9)
| ((CBENONDA & 0x1) << 15)
| ((VLIDX & 0xFFF) << 17)
| ((SA_POLICE & 0x1) << 29)
| ((DA_POLICE & 0x1) << 30);
uint32_t status = 0;
do {
Std_ReturnType ret = spi_read_reg(L2_ADDRESS_CTL, &status);
if (ret != E_OK) {
return -1;
}
} while ((status >> VALID_BIT_SHIFT) & 1U);
for (int i = 0; i < ADDRESS_RANGE; i++) {
Std_ReturnType ret = spi_write_reg(L2_ADDRESS + i, l2Entry[i]);
if (ret != E_OK) {
return -2;
}
uint32_t readBack = 0;
ret = spi_read_reg(L2_ADDRESS + i, &readBack);
if (ret != E_OK || readBack != l2Entry[i]) {
return -3;
}
}
uint32_t controlL2Entry = 0;
controlL2Entry |= (VALID << VALID_BIT_SHIFT);
controlL2Entry |= (RDWRSET << RDWRSET_BIT_SHIFT);
controlL2Entry |= (ERROR << ERROR_BIT_SHIFT);
controlL2Entry |= (LOCKED << LOCKED_BIT_SHIFT);
controlL2Entry |= (VALIDENT << VALIDENT_BIT_SHIFT);
controlL2Entry |= (MGMTROUTE << MGMTROUTE_BIT_SHIFT);
controlL2Entry |= (HOSTCMD << HOSTCMD_BIT_SHIFT);
Std_ReturnType ret = spi_write_reg(L2_ADDRESS_CTL, controlL2Entry);
if (ret != E_OK) {
return -4;
}
do {
ret = spi_read_reg(L2_ADDRESS_CTL, &status);
if (ret != E_OK) {
return -5;
}
} while ((status >> VALID_BIT_SHIFT) & 1U);
return 0;
}
This is my revised code implementation
can you verify once this implementation will bring a Dynamic Entry. so i tried debugging it by readingback the register value whether the values are getting written into the register i could able to see the values are written into the register but i am not sure whether the enrty is created or not ?
and also the reflection of Dynamic entry of L2 lookup table is not getting reflected ?
so how can i verify the L2 lookup table enrty is created ?
Hello @Atkinson ,
1. Bit Shift Syntax (<<, >>)
It appears that some of the bit shift operators in your code were replaced with HTML entities like << and >>. This typically happens when code is copied into web-based platforms. Anyway, please check it directly in your code.
#define L2_CTRL_VALID_BIT (1U << 31)
should be:
#define L2_CTRL_VALID_BIT (1U << 31)
2. Entry Issue
There are several syntax errors and missing shift values in the CreateL2Entry() function, particularly in entry[3], entry[4], and entry[5]. These may result in incomplete or invalid register writes.
e.g.
I recommend comparing your implementation against the Python reference script, which correctly constructs all six 32-bit words of the L2 entry.
Best regards,
Pavel
Hi @PavelL
3.)I have changed the Register address to #define L2_ENTRY0_ADDR 0x00000023
but still i am not able to identify the table which i am trying to add getting reflected when i transmit the message
"If I understand correctly, you're referring to the L2 Lookup Table described in Table 1182 of UM11107. This table consists of six 32-bit registers per entry. You can see this structure reflected in the debug output from the Python script."
So i am facing up with the issue in a way where i am trying to write the data into address which need to create a Dynamic Entry in L2 lookup table but its not creating a Dynamic Entry
#define L2_ENTRY0_ADDR 0x00000023
#define L2_ENTRY1_ADDR 0x00000024
#define L2_ENTRY2_ADDR 0x00000025
#define L2_ENTRY3_ADDR 0x00000026
#define L2_ENTRY4_ADDR 0x00000027
#define L2_ENTRY5_ADDR 0x00000028
#define L2_CTRL_ADDR 0x00000029
#define L2_CTRL_VALID_BIT (1U << 31)
#define L2_CTRL_WR_RDB_BIT (1U << 30)
#define L2_CTRL_VALIDENTRY_BIT (1U << 28)
#define L2_CTRL_HOSTCMD_SHIFT 23
#define L2_CTRL_HOSTCMD_WRITE (0b011U << L2_CTRL_HOSTCMD_SHIFT)
Std_ReturnType CreateL2Entry(uint32_t entry[6])
{
uint16_t INDEX = 11;
uint64_t MACADDR = 0x661122334455;
uint64_t MACADDR_MASK = 0xFFFFFFFFFFFF;
uint16_t VLANID = 3;
uint16_t VLANID_MASK = 0xFFF;
uint8_t IOTAG = 0;
uint8_t IOTAG_MASK = 0;
uint8_t SRCPORT = 5;
uint8_t SRCPORT_MASK = 0xF;
uint8_t DESTPORTS = 0x02;
uint8_t ENFPORT = 1;
uint8_t RETAG = 0;
uint8_t MIRROR = 0;
uint8_t TAKETS = 0;
uint16_t MIRR_VLAN = 0xFFF;
uint8_t TRAPTOHOST = 0;
uint8_t CBSIDIN = 0x3F;
uint8_t CBENONDA = 0;
uint16_t VLIDX = 0xFFF;
uint8_t SA_POLICE = 0;
uint8_t DA_POLICE = 0;
entry[0] = ((INDEX & 0x03FF) << 1) |
((ENFPORT & 0x1) << 11) |
((DESTPORTS & 0x7FF) << 12) |
((SRCPORT & 0xF) << 23) |
((MACADDR & 0x1F) << 27);
entry[1] = (uint32_t)((MACADDR >> 5) & 0xFFFFFFFF);
entry[2] = (((MACADDR >> 37) & 0x7FF) << 0) |
((VLANID & 0xFFF) << 11) |
((IOTAG & 0x1) << 23) |
((MACADDR_MASK & 0xFF) << 24);
entry[3] = (uint32_t)((MACADDR_MASK >>
entry[4] = (((MACADDR_MASK >> 40) & 0xFF) << 0) |
((VLANID_MASK & 0xFFF) <<
((IOTAG_MASK & 0x1) << 20) |
((SRCPORT_MASK & 0xF) << 21) |
((RETAG & 0x1) << 25) |
((MIRROR & 0x1) << 26) |
((TAKETS & 0x1) << 27) |
((MIRR_VLAN & 0xF) << 28);
entry[5] = (((MIRR_VLAN >> 4) & 0xFF) << 0) |
((TRAPTOHOST & 0x1) <<
((CBSIDIN & 0x3F) << 9) |
((CBENONDA & 0x1) << 15) |
((VLIDX & 0xFFF) << 17) |
((SA_POLICE & 0x1) << 29) |
((DA_POLICE & 0x1) << 30);
return E_OK;
}
Std_ReturnType WriteL2LookupEntry(uint8_t swt)
{
if (swt > 0) return E_NOT_OK;
Std_ReturnType ret;
uint32_t ctrlReg = 0;
uint32_t entry[6];
// Step 1: Create the entry
ret = CreateL2Entry(entry);
if (ret != E_OK) return ret;
// Step 2: Wait for VALID bit to clear
do {
ret = spi_read_reg(L2_CTRL_ADDR, &ctrlReg);
if (ret != E_OK) return ret;
} while (ctrlReg & L2_CTRL_VALID_BIT);
// Step 3: Write entry data to registers
ret = spi_write_reg(L2_ENTRY0_ADDR, entry[0]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY1_ADDR, entry[1]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY2_ADDR, entry[2]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY3_ADDR, entry[3]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY4_ADDR, entry[4]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY5_ADDR, entry[5]); if (ret != E_OK) return ret;
// Step 4: Set control register to trigger write
uint32_t ctrlValue = (1U << 31) |
(1U << 30) |
(1U << 28) |
(1U << 27) |
(3U << 23);
ret = spi_write_reg(L2_CTRL_ADDR, ctrlValue);
if (ret != E_OK) return ret;
do {
ret = spi_read_reg(L2_CTRL_ADDR, &ctrlReg);
if (ret != E_OK) return ret;
} while (ctrlReg & L2_CTRL_VALID_BIT);
return E_OK;
}
can you check once whether any issues is there because its not creating Dynamic Entry for the L2 lookup Table ?
Hello @Atkinson ,
1. Yes, the host tools were originally developed for the SJA1110-EVM board. I personally use these scripts with debug prints to verify my understanding of register access. While you cannot run them directly on the S32G-VNP-RDB2 platform, the underlying logic can be adapted, since the S32G accesses the SJA1110 via SPI.
2. If you're looking for SPI read/write implementations for the S32G platform, you have two main options:
3. If I understand correctly, you're referring to the L2 Lookup Table described in Table 1182 of UM11107. This table consists of six 32-bit registers per entry. You can see this structure reflected in the debug output from the Python script.
Best regards,
Pavel
Hi @PavelL
I am having some certain questions
1.) This Debug print of l2_write_lookup_table.py was taken from SJA1110 EVM Board Implementation ryt its not from S32G-VNP RDB Board? and we can't implement l2_write_lookup_table.py in S32G-VNP-RDB32 Board directly
2.)can you provide me the code of SPI_read and Spi_write for integrating with the above code?
because in through the implementation in S32G writing it via SPI_Read and SPI_write is doing the operation but unable to see the Dynamic Entry getting added
Std_ReturnType spi_read_reg(uint32_t regAddress, uint32_t* regValue)
{
Std_ReturnType ret;
uint32_t TxChBuf[65] = {0};
uint32_t RxChBuf[65] = {0};
uint16_t regCount = 1;
Siul2_Dio_Ip_WritePin(SPI_MCU_ES1_CS_AP_PORT, SPI_MCU_ES1_CS_AP_PIN, 0U);
ret = getSpiAPCommand(false, regAddress, regCount, TxChBuf, regValue);
if (ret != E_OK) {
Siul2_Dio_Ip_WritePin(SPI_MCU_ES1_CS_AP_PORT, SPI_MCU_ES1_CS_AP_PIN, 1U);
return ret;
}
ret=Spi_SetupEB(5u, (uint8_t*)TxChBuf, (uint8_t*)RxChBuf, 8u);
if (ret == E_OK) {
ret=Spi_SyncTransmit(SpiConf_SpiSequence_SpiSequence_sja);
if (ret == E_OK) {
*regValue = RxChBuf[1];
}
}
Siul2_Dio_Ip_WritePin(SPI_MCU_ES1_CS_AP_PORT, SPI_MCU_ES1_CS_AP_PIN, 1U);
return ret;
}
Std_ReturnType spi_write_reg(uint32_t regAddress, uint32_t regValue)
{
Std_ReturnType ret;
uint32_t TxChBuf[65] = {0};
uint32_t RxChBuf[65] = {0};
uint16_t regCount = 1;
uint32_t regValArray[1] = { regValue };
// Pull chip select LOW
Siul2_Dio_Ip_WritePin(SPI_MCU_ES1_CS_AP_PORT, SPI_MCU_ES1_CS_AP_PIN, 0U);
ret = getSpiAPCommand(true, regAddress, regCount, TxChBuf, regValArray);
if (ret != E_OK) {
Siul2_Dio_Ip_WritePin(SPI_MCU_ES1_CS_AP_PORT, SPI_MCU_ES1_CS_AP_PIN, 1U);
return ret;
}
ret=Spi_SetupEB(5u, (uint8_t*)TxChBuf, (uint8_t*)RxChBuf, 8u);
if (ret == E_OK) {
ret=Spi_SyncTransmit(SpiConf_SpiSequence_SpiSequence_sja);
}
Siul2_Dio_Ip_WritePin(SPI_MCU_ES1_CS_AP_PORT, SPI_MCU_ES1_CS_AP_PIN, 1U);
return ret;
}
Correct me if i am wrong !!
3.)I have changed the Register address to #define L2_ENTRY0_ADDR 0x00000023
but still i am not able to identify the table which i am trying to add getting reflected when i transmit the message
Hello @Atkinson ,
your code looks good. However, if you want to access dynamic tables by SPI, the entry addresses are without FF and shifted right by 2. As I already mentioned in some of my previous post.
So, #define L2_ENTRY0_ADDR 0xFF00008c should be #define L2_ENTRY0_ADDR 0x00000023
But it depends, what your routine spi_write_reg actually does.
Here is output of the write_l2_lookup_table.py , incl. my debug prints (tx/rx refers to data what would be seen on SPI wires by oscilloscope or by logic analyzer):
DBG spi_tx: ['0x02000290', '0xAABBCCDD']
DBG spi_rx: ['0x02000290', '0x00000000']
DBG spi_read 0x0029 -> 0x00000000
DBG spi_write 0x0023 <- 0x37FFFA00
DBG spi_tx: ['0x80000230', '0x37FFFA00']
DBG spi_write 0x0024 <- 0x119A22AB
DBG spi_tx: ['0x80000240', '0x119A22AB']
DBG spi_write 0x0025 <- 0xFF807089
DBG spi_tx: ['0x80000250', '0xFF807089']
DBG spi_write 0x0026 <- 0xFFFFFFFF
DBG spi_tx: ['0x80000260', '0xFFFFFFFF']
DBG spi_write 0x0027 <- 0xF1FFFFFF
DBG spi_tx: ['0x80000270', '0xF1FFFFFF']
DBG spi_write 0x0028 <- 0x1FFE7EFF
DBG spi_tx: ['0x80000280', '0x1FFE7EFF']
DBG spi_write 0x0029 <- 0xD9800000
DBG spi_tx: ['0x80000290', '0xD9800000']
+-------+-------------+-------------------+--------+---------+-------+
| INDEX | DESTPORTS | MACADDR | VLANID | ENFPORT | IOTAG |
+-------+-------------+-------------------+--------+---------+-------+
| 256 | 11111111111 | 11:22:33:44:55:66 | 14 | 1 | 1 |
+-------+-------------+-------------------+--------+---------+-------+
L2 Lookup Table Entry Written (SW1)
Regarding the FAE request, FAE is not on my escalation path. Your company has assigned FAE, so you can contact FAE directly.
Best regards,
Pavel
Hi @PavelL
#define L2_ENTRY0_ADDR 0xFF00008c
#define L2_ENTRY1_ADDR 0xFF000090
#define L2_ENTRY2_ADDR 0xFF000094
#define L2_ENTRY3_ADDR 0xFF000098
#define L2_ENTRY4_ADDR 0xFF00009C
#define L2_ENTRY5_ADDR 0xFF0000A0
#define L2_CTRL_ADDR 0xFF0000A4
Std_ReturnType CreateL2Entry(uint32_t entry[6])
{
uint16_t INDEX = 11;
uint64_t MACADDR = 0x661122334455;
uint64_t MACADDR_MASK = 0xFFFFFFFFFFFF;
uint16_t VLANID = 3;
uint16_t VLANID_MASK = 0xFFF;
uint8_t IOTAG = 0;
uint8_t IOTAG_MASK = 0;
uint8_t SRCPORT = 5;
uint8_t SRCPORT_MASK = 0xF;
uint8_t DESTPORTS = 0x02;
uint8_t ENFPORT = 1;
uint8_t RETAG = 0;
uint8_t MIRROR = 0;
uint8_t TAKETS = 0;
uint16_t MIRR_VLAN = 0xFFF;
uint8_t TRAPTOHOST = 0;
uint8_t CBSIDIN = 0x3F;
uint8_t CBENONDA = 0;
uint16_t VLIDX = 0xFFF;
uint8_t SA_POLICE = 0;
uint8_t DA_POLICE = 0;
entry[0] = ((INDEX & 0x03FF) << 1) |
((ENFPORT & 0x1) << 11) |
((DESTPORTS & 0x7FF) << 12) |
((SRCPORT & 0xF) << 23) |
((MACADDR & 0x1F) << 27);
entry[1] = (uint32_t)((MACADDR >> 5) & 0xFFFFFFFF);
entry[2] = (((MACADDR >> 37) & 0x7FF) << 0) |
((VLANID & 0xFFF) << 11) |
((IOTAG & 0x1) << 23) |
((MACADDR_MASK & 0xFF) << 24);
entry[3] = (uint32_t)((MACADDR_MASK >>
entry[4] = (((MACADDR_MASK >> 40) & 0xFF) << 0) |
((VLANID_MASK & 0xFFF) <<
((IOTAG_MASK & 0x1) << 20) |
((SRCPORT_MASK & 0xF) << 21) |
((RETAG & 0x1) << 25) |
((MIRROR & 0x1) << 26) |
((TAKETS & 0x1) << 27) |
((MIRR_VLAN & 0xF) << 28);
entry[5] = (((MIRR_VLAN >> 4) & 0xFF) << 0) |
((TRAPTOHOST & 0x1) <<
((CBSIDIN & 0x3F) << 9) |
((CBENONDA & 0x1) << 15) |
((VLIDX & 0xFFF) << 17) |
((SA_POLICE & 0x1) << 29) |
((DA_POLICE & 0x1) << 30);
return E_OK;
}
Std_ReturnType WriteL2LookupEntry(uint8_t swt)
{
if (swt > 0) return E_NOT_OK;
Std_ReturnType ret;
uint32_t ctrlReg = 0;
uint32_t entry[6];
ret = CreateL2Entry(entry);
if (ret != E_OK) return ret;
do {
ret = spi_read_reg(L2_CTRL_ADDR, &ctrlReg);
if (ret != E_OK) return ret;
} while (ctrlReg & (1U << 31));
ret = spi_write_reg(L2_ENTRY0_ADDR, entry[0]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY1_ADDR, entry[1]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY2_ADDR, entry[2]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY3_ADDR, entry[3]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY4_ADDR, entry[4]); if (ret != E_OK) return ret;
ret = spi_write_reg(L2_ENTRY5_ADDR, entry[5]); if (ret != E_OK) return ret;
uint32_t ctrlValue = (1U << 31) | (1U << 30) | (1U << 28) | (1U << 27) | (3U << 23);
ret = spi_write_reg(L2_CTRL_ADDR, ctrlValue);
if (ret != E_OK) return ret;
do {
ret = spi_read_reg(L2_CTRL_ADDR, &ctrlReg);
if (ret != E_OK) return ret;
} while (ctrlReg & (1U << 31));
return E_OK;
}
This was the current implementation for Creating a Dynamic Entry can you cross verify with FAE whether this implementation is correct !!
Any other changes need to be done
Hello @Atkinson ,
Answers to your question are related to your application use case.
You can create a dynamic entry absolutely anywhere, also in S32G or in SJA1110:
There are examples how to write dynamic entry to the switchcore of SJA1110:
Bets regards,
Pavel
Hi @PavelL
1.) So this change of accessing the register for Dynamic configuration need to be done in S32G or SJA1110 switch ?
2.)if via SJA1110 switch whether reading and writing the register by SPI need to be performed and where this implementation need to be carried in between SWITCH_DRV?
3.) where we can create our Dynamic entry so that it will get append in switch
More Explanation would be great help!!
Hello @Atkinson ,
I apologize for late response caused my current workload.
I understand it might not seem straightforward at first, but the concept is actually quite simple.
The final register address depends on the access method - whether you're accessing it internally via the M7 core over AHB, or externally via the SPI_AP interface. The base addresses differ depending on the access point.
The register address is calculated using the base address provided in Table 2 of UM11107. For example, for the switch subsystem, the base address is 0x00000000 for SPI_AP access and 0xFF000000 for AHB access.
In your case, the register 0x8C (DYN_BE_LKP_ENTRY0) becomes 0xFF00008C when accessed via AHB.
Now, here’s where it might get confusing: the SDK driver defines the following:
Hi @PavelL
can you give me the complete address for Reading and writing
Address Name Access Reset Description
8Ch DYN_BE_LKP_
ENTRY0
R/W 0h
(FFFFFFFEh)
before 8ch what would be the base address?
like for example it will have 0x3fc0008c so for this dynamic entry table we will have a complete address can you give me the complete address for
8Ch DYN_BE_LKP_
ENTRY0
R/W 0h
(FFFFFFFEh)
90h DYN_BE_LKP_
ENTRY1
R/W 0h
94h DYN_BE_LKP_
ENTRY2
R/W 0h
98h DYN_BE_LKP_
ENTRY3
R/W 0h
9Ch DYN_BE_LKP_
ENTRY4
R/W 0h
A0h DYN_BE_LKP_
ENTRY5
R/W 0h
A4h DYN_BE_LKP_CTRL R/W 0h
(FF800000h)
Hello @Atkinson ,
I'm not aware of any supporting documents - that's a general knowledge, device independent. I have consulted this topic with Copilot and it should be possible to modify sja11xx_ftdi4232.py to use DSPI5 instead of FTDI.
Best regards,
Pavel
Hi @PavelL
To use this script with the S32G-VNP-RDB2 or RDB3 board, the FTDI layer would need to be modified to communicate via DSPI5 on the S32G processor.
How to do that? is there any supporting documents?
Hello @Atkinson ,
Q1: "So since the Linux BSP is pre-installed on the SD Card... will it enable dynamic configuration automatically?"
A1: No, the BSP includes the DSA driver, which supports dynamic configuration, but it is not automatically enabled for all use cases.
Q2: "For configuring the L2 lookup entry via Dynamically using the write_l2_lookup.py (python evm host tools) whether that process remains same ?or for S32G-VNP-RDB board it will change"
A2: The general flow for creating and uploading a dynamic entry remains the same, as described in UM11107 and AN12925.
However, the write_l2_lookup_table.py script uses the FTDI driver to access the SJA1110-EVM via USB.
To use this script with the S32G-VNP-RDB2 or RDB3 board, the FTDI layer would need to be modified to communicate via DSPI5 on the S32G processor.
Q3: "Whether it is possible to interface Laptop & S32G-VNP_RDB32 board creating a Dynamic entry?"
A3: Yes, it is possible. You are free to develop custom software running on Linux to handle your own protocol over SPI or Ethernet to configure dynamic entries.
Q4: "and one more thing whether SJA1110_SDK_1.0.2 is a main thing needed for implementing RTM1.0.0 would that be fine?"
A4: No, it is recommended to use the latest version of SJA1110 SDK 1.0.2 for compatibility and stability.
For further support, please contact your FAE or submit a ticket via https://support.nxp.com/s/?language=en_US.
Thank you for your understanding.
Best regards,
Pavel
Hi @PavelL
So since the Linux BSP is pre-installed on the SD Card if the SD card is getting inserted into the S32G-VNP-RDB board. so whether the BSP which present in SD card which will be enabling Dynamic configuration automatically
For configuring the L2 lookup entry via Dynamically using the write_l2_lookup.py (python evm host tools) whether that process remains same ?or for S32G-VNP-RDB board it will change
If the Process remains same how can we interface laptop & RDB32Board when interfacing it via Laptop and SJA1110 EVM board it will be connected using USB interface which will help to configure it Dynamic entry So in the case of S32G-VNP-RDB32 Board How the interface will be?
Whether it is possible to interface Laptop & S32G-VNP_RDB32 board creating a Dynamic entry?
and one more thing whether SJA1110_SDK_1.0.2 is a main thing needed for implementing RTM1.0.0 would that be fine ?
Hello @Atkinson ,
The Linux BSP is already pre-installed on the SD card delivered with the S32G-VNP-RDB3 board, so no additional action is required in that regard.
I assume you have successfully brought up the board (apologies if you already mentioned this in previous messages).
The BSP includes the SJA1110 DSA driver, which provides a range of useful features — including support for dynamic L2 lookup table configuration.
The DSA driver is enabled by default.
As this case has evolved into a multi-domain topic, I’ve reached out to your FAE to discuss how we should proceed further.
Best regards,
Pavel
Hi @PavelL
I am currently trying to implement it in S32G-VNP-RDB32 -- SJA1110 Switch
currently, I don't have any firmware on my own
Which Linux BSP need to be utilized
What might be the complete changes need to be done
Hello @Atkinson ,
If you're planning to do this from the S32G-VNP-RDB3 board, that changes things significantly. Will you be running the Linux BSP on the board, or do you plan to use your own firmware?
Best regards,
Pavel
Hi @PavelL
3. SPI_HAP (Host Access Protocol)
The write_l2_lookup_table.py script from the host tools demonstrates how to configure the L2 table dynamically via SPI. This approach can be used by any external MCU or host processor. The logic from this script can also be ported to run directly on the internal M7 core if needed.
I am looking to configure an entry via write_l2_lookup_table.py
via how the configuration which we written in write_l2_lookup_table.py file will be moving to the RDB32 Board
what interface need to be done from laptop to RDB32Board?
what are the communication mechanism through which we can pass our l2 lookup entry to the device (RDB32 Board)
can you give me a procedure or where i can find the procedure for implementation
Hello @Atkinson ,
Yes, you're right — when the system boots, the static configuration is automatically loaded from external flash.
Let me clarify the concept of dynamic configuration in more detail.
The task of dynamic configuration can be split into two parts:
There are three interfaces that can be used for this:
Best regards,
Pavel
Hi @PavelL
3. SPI_HAP (Host Access Protocol)
The write_l2_lookup_table.py script from the host tools demonstrates how to configure the L2 table dynamically via SPI. This approach can be used by any external MCU or host processor. The logic from this script can also be ported to run directly on the internal M7 core if needed.
I am looking to configure an entry via write_l2_lookup_table.py
via how the configuration which we written in write_l2_lookup_table.py file will be moving to the RDB32 Board
what interface need to be done from laptop to RDB32Board?
what are the communication mechanism through which we can pass our l2 lookup entry to the device (RDB32 Board)
can you give me a procedure or where i can find the procedure for implementation
Hello @PavelL
As you mentioned I would like to configure the switch_core configuration Dynamically
When a System boots the static configuration which we configured in peripherals is getting loaded
I am looking in a way to configure the Table entry which are configured in Static needs to be configured it via, Dynamically.
In either case, there are generally three ways to update the switch configuration dynamically:
if these are the only possible way, then configuring it in as a code which i provided will work or will not work ?
and also the three process which mentioned here is it same as the ECT & write_l2_lookup_table.py? or these are different one !!
Hello @Atkinson ,
Could you please clarify your use case for using a dynamic L2 lookup table?
Personally, I would consider using dynamic entries in two typical scenarios:
In either case, there are generally three ways to update the switch configuration dynamically:
Does your use case fall into one of these categories, or are you aiming for something different?
Best regards,
Pavel
Hello @PavelL
As you mentioned it i tried incorporating the code
can you confirm where do i need to place the code which will help us in creating the entry
Earlier Message you have mentioned there were Two Methods via doing it using ECT (Ethernet configuration Tool) and SPI_HAP
so these tools and files are needed to process on run -time ryt ??
The code which i shared is which i am trying to implement it without these tools
I am looking to create a Dynamic L2 Lookup Entry whether it is fine to do ??
Regards
Atkinson
Hello @Atkinson ,
I apologize for the delayed response due to public holidays.
Thank you for sharing your code. I had a quick look, and at first glance, it appears to align with the principles outlined in AN12925, chapter 5.1.3.2 Writing a L2 Lookup table entry. I also noticed that the #define statements seem to be reused from SJA1110.h, which is standard practice.
Please note, however, that an in-depth custom code review goes beyond the scope of our complimentary online support. Additionally, the timing and context in which this function should be called are highly application-specific and may vary depending on your system architecture and requirements.
Best regards,
Pavel
Hello @PavelL
static status_t wait_for_valid_clear(uint8_t swt) {
status_t status;
uint32_t ctrl = 0;
do {
status = SWITCH_DRV_Read(swt, 1, DYN_BE_LKP_CTRL, &ctrl);
if (status != STATUS_SUCCESS) return status;
} while (ctrl & DYN_BE_LKP_CTRL_VALID_MASK);
if (ctrl & DYN_BE_LKP_CTRL_ERROR_MASK) return STATUS_ERROR;
return STATUS_SUCCESS;
}
status_t SJA1110_AddL2LookupEntry_Dynamic(uint8_t swt) {
status_t status;
// MAC = 66:11:22:33:44:55
uint8_t mac[6] = {0x66, 0x11, 0x22, 0x33, 0x44, 0x55};
uint64_t mac64 = ((uint64_t)mac[0] << 40) | ((uint64_t)mac[1] << 32) | ((uint64_t)mac[2] << 24) |
((uint64_t)mac[3] << 16) | ((uint64_t)mac[4] <<
// Prepare L2 lookup entry fields
uint32_t entry[WORD_COUNT] = {0};
entry[0] = ((5 & 0xF) << 24) | (1 << 11); // SrcPort, DestPorts 1 & 2
entry[1] = (uint32_t)(mac64 & 0xFFFFFFFF);
entry[2] = (uint32_t)((mac64 >> 32) & 0xFFFF);
entry[2] |= (3 << 16); // VLAN ID = 3
entry[3] = 0; // Optional masks
entry[4] = 0;
entry[5] = (8 << 4) | (1 << 10); // CB_SID_IN = 8, CB_ON_DA = 1
// Wait until previous operation completes
status = wait_for_valid_clear(swt);
if (status != STATUS_SUCCESS) return status;
// Write L2 entry data to registers
status = SWITCH_DRV_Write(swt, WORD_COUNT, DYN_BE_LKP_ENTRY0, entry);
if (status != STATUS_SUCCESS) return status;
// Construct control register value
uint32_t ctrl = DYN_BE_LKP_CTRL_VALID_MASK |
DYN_BE_LKP_CTRL_RDWRSET_MASK |
(HOSTCMD_WRITE << DYN_BE_LKP_CTRL_HOSTCMD_SHIFT);
// Trigger write
status = SWITCH_DRV_Write(swt, 1, DYN_BE_LKP_CTRL, &ctrl);
if (status != STATUS_SUCCESS) return status;
// Wait for operation to complete
return wait_for_valid_clear(swt);
}
Whether this code can be utilized for generating a Dynamic entry ?
if this code is correct, where the function needs to be called??
Regards
Atkinson
Hi @PavelL
#include
#include
// Define register addresses as per your SoC mapping
#define DYN_BE_LKP_ENTRY0 0x000 // Replace with actual address
#define DYN_BE_LKP_ENTRY1 0x004
#define DYN_BE_LKP_ENTRY2 0x008
#define DYN_BE_LKP_ENTRY3 0x00C
#define DYN_BE_LKP_ENTRY4 0x010
#define DYN_BE_LKP_ENTRY5 0x014
#define DYN_BE_LKP_CTRL 0x018
void insert_l2_unicast_entry(void)
{
// === INPUTS ===
uint64_t mac = 0x021122334455ULL; // MAC address
uint16_t vlan_id = 3;
uint8_t dest_port = 3; // Port 3
uint16_t dest_ports_mask = (1 << dest_port);
uint16_t index = 5;
// === STEP 1: Wait for previous operation to complete ===
while (read_register(DYN_BE_LKP_CTRL) & ETHSW_DYN_BE_LKP_CTRL_bValid_MASK);
// === STEP 2: Prepare entry fields ===
uint32_t mac_lo = (uint32_t)(mac & 0xFFFFFFFF); // Lower 32 bits
uint32_t mac_hi = (uint32_t)((mac >> 32) & 0xFFFF); // Upper 16 bits
uint32_t entry0 = ETHSW_DYN_BE_LKP_ENTRY0_uvEntry_30__0_(0x00000000);
uint32_t entry1 = ETHSW_DYN_BE_LKP_ENTRY1_uvEntry_62__31_(mac_lo);
uint32_t entry2 = ETHSW_DYN_BE_LKP_ENTRY2_uvEntry_94__63_(mac_hi);
uint32_t entry3 = ETHSW_DYN_BE_LKP_ENTRY3_uvEntry_126__95_(
((vlan_id & 0xFFF) << 16) | (dest_ports_mask & 0xFFFF));
uint32_t entry4 = ETHSW_DYN_BE_LKP_ENTRY4_uvEntry_158__127_(0x00000000); // optional flags/reserved
uint32_t entry5 = ETHSW_DYN_BE_LKP_ENTRY5_uvEntry_190__159_(index);
// === STEP 3: Write entries to DYN registers ===
write_register(DYN_BE_LKP_ENTRY0, entry0);
write_register(DYN_BE_LKP_ENTRY1, entry1);
write_register(DYN_BE_LKP_ENTRY2, entry2);
write_register(DYN_BE_LKP_ENTRY3, entry3);
write_register(DYN_BE_LKP_ENTRY4, entry4);
write_register(DYN_BE_LKP_ENTRY5, entry5);
// === STEP 4: Trigger WRITE via control register ===
uint32_t ctrl_val = 0;
ctrl_val |= ETHSW_DYN_BE_LKP_CTRL_bValid(1);
ctrl_val |= ETHSW_DYN_BE_LKP_CTRL_bWrRdB(1); // Write operation
ctrl_val |= ETHSW_DYN_BE_LKP_CTRL_uvHostCommand(0b011); // Host write
ctrl_val |= ETHSW_DYN_BE_LKP_CTRL_bValidEntry(1); // Valid entry
write_register(DYN_BE_LKP_CTRL, ctrl_val);
// === STEP 5: Wait for operation to complete ===
while (read_register(DYN_BE_LKP_CTRL) & ETHSW_DYN_BE_LKP_CTRL_bValid_MASK);
// === STEP 6: Check for error ===
uint32_t status = read_register(DYN_BE_LKP_CTRL);
if (status & ETHSW_DYN_BE_LKP_CTRL_bError_MASK) {
// Handle the error
// You can print or log this as needed
}
}
whether can i insert this code for Dynamically Writing the data to L2 Lookup Table
Where should i insert this code to test ?
Looking forward for your reply
Regards
Atkinson
Hi @PavelL
In the Method of configuring via SPI_HAP
where can i find the write_l2_lookup_table.py?
How this needs to be communicated via SPI_HAP? when after the device bootup
In Previously during the static configuration, we have configured it each data via each index?
so whether data if static initially configured is fine or none of the static configuration its shouldn't be connected?
whether this write_l2_lookup_table.py can you share it?
when running this script from where (which port it need to be connected to pass the data) the script will be polling the data?
Regards
Atkinson
Hello @Atkinson ,
The method of dynamic configuration depends on your use case — it can be performed either by the internal M7 core or externally via SPI. Below is a list of possible solutions:
1. ECT (Ethernet Control Tool)
Dynamic configuration via external Ethernet (UDP) frames. The internal firmware running on the M7 core receives the configuration and applies it to the switch core.
2. Modified ECT
The ECT source code can be adapted to accept any Ethernet frame format (not just UDP). This allows integration into custom protocols or systems. The internal logic remains the same — the M7 core performs the actual register updates.
3. SPI_HAP (Host Access Protocol)
The write_l2_lookup_table.py script from the host tools demonstrates how to configure the L2 table dynamically via SPI. This approach can be used by any external MCU or host processor. The logic from this script can also be ported to run directly on the internal M7 core if needed.
All of the above methods follow the same principle described in AN12925, section 5.1.3.2. The register-level flow is identical and includes polling the control register, writing to the entry registers, and triggering the update via the control register.
Best regards,
Pavel