Skip to content

Conversation

@vDorst
Copy link
Collaborator

@vDorst vDorst commented Dec 15, 2025

I am working on SIPHASH were I need a large memory block of 32 bytes.
But I want to access it easily.
I know you can access lower part XDATA via a single register using the instruction MOVX @Ri, A.
This is called Paged Memory??. Just use __pdata to define a variable.
Not sure if paging is controllable.

Because we are using sfr_data a lot, it make sense to make it faster to access.
I made a quick test to move sfr_data to pdata.
Because of some function I also needed to move linkbits_last.
See first commit.
As a result it reduced the code size with 350 bytes.
Most of these bytes, I assume, are used to load in the DPTR of calculate it.

Second commit is move sfr_data and linkbits_last to sram.
I think it make sense to use some extra sram to access sfr_data faster.
As a result it reduced the code size with 650 bytes from the original xdata code.

diff between the two .mem-files.
Screenshot_20251215_232627

So I think we can optimize more things by moving them into __pdata.

And/Or define a large scrats-memory block in pdata, which I may need for my SIPHASH.
Which can also be used by other code.

@logicog
Copy link
Owner

logicog commented Dec 16, 2025

This is a great idea! It could optimize both memory use and speed!

I was wondering: we seem to be using 143-128 = 15 bytes of SRAM more, but I assume this may be (more than?) compensated by using less memory on the stack. How does this compare?

Do we know whether it is possible to access PDATA in different pages? I am referring to https://www.keil.com/support/docs/1848.htm
There are a couple of SFRs on the rtl8372/3 that could qualify for what they call XPAGE register. In the startup code we have a couple of SFRs where we do not understand what they do:

SFR_97 = 0;	// HADDR?
// Set in managed mode:
SFR_b9 = 0x00;
SFR_ba = 0x80;

Do you think one of them could be a page register for pdata access? My understanding is that the usual register P2= SFR_a0 is used for something else on the SoCs.

@vDorst
Copy link
Collaborator Author

vDorst commented Dec 16, 2025

From the DesignWare DW8051 MacroCell Solution Databook at External RAM

To replace the function of the Port 2 latch in designs that do not use
a Port 2 module, the DW8051 provides an additional special function
register, MPAGE, at SFR address 92h. During MOVX A, @Ri and
MOVX @Ri, A instructions, the DW8051 places the contents of the
MPAGE register on the upper 8 address bits (mem_addr[15:8]). This
provides the paging function that is normally provided by the Port 2
latch. The MPAGE register has no function when a Port 2 module is
used to connect external RAM. If the external RAM is connected to the mem_addr outputs, you may
need to adapt existing software that was written for the standard
memory interface. If the software uses the paging function of the
instructions MOVX A, @Ri and MOVX @Ri, A, change the address
of the register holding the memory page from A0h (Port 2) to 92h
(MPAGE). Using SFR address 92h instead of the PORT2 register at SFR
address A0h leaves the bit-addressable A0h SFR address available
for other operations.

So it looks like MPAGE register 0x92 when implemented.
We have to test that.

@logicog
Copy link
Owner

logicog commented Dec 16, 2025

I had also looked into the DW8051 documentation but made the mistake of grepping only for XPAGE. Great that you found this! It appears, sdcc does have some support for such a register for code generation: https://k1.spdns.de/Develop/Hardware/AVR/mixed%20docs.../doc/sdccman.html/node104.html
This is very promising!
It leaves still the mystery what registers 97, b9 and ba actually do...

@vDorst
Copy link
Collaborator Author

vDorst commented Dec 16, 2025

Keeplink KP-9000-9XHM-X firmware is using page memory too.

Screenshot_20251216_205544

Looking for the MPAGE register.
MOV 92, #val is 75 92 .. in bytes but Ghidra doesn't return any results.
MOV 92, A is f5 92 one hit in bank21. But not decodes yet, so can be anything. I also can't find any code that is using the bank_switch_to_21 code.
INC 92 is 05 92 has 4 hits in bank21. But not decodes yet.

@vDorst
Copy link
Collaborator Author

vDorst commented Dec 16, 2025

diff --git a/rtl837x_sfr.h b/rtl837x_sfr.h
index 9a99ac7..5db408a 100644
--- a/rtl837x_sfr.h
+++ b/rtl837x_sfr.h
@@ -56,6 +56,11 @@ __sfr __at(0x97) SFR_97;     // HADDR?
 __sfr __at(0xb9) SFR_b9;
 __sfr __at(0xba) SFR_ba;
 
+
+// SFR to control the XDATA PAGED high value
+// Used for instruction `movx @Ri,a` and `movx a,@Ri`
+__sfr __at(0x92) SFR_MPAGE;
+
 // Clause 22 PHY access ???
 __sfr __at(0x93) SFR_93;
 __sfr __at(0x94) SFR_94;
diff --git a/rtlplayground.c b/rtlplayground.c
index 7beb6b3..114295a 100644
--- a/rtlplayground.c
+++ b/rtlplayground.c
@@ -19,6 +19,7 @@
 #include "machine.h"
 
 extern __code const struct machine machine;
+__pdata uint8_t PAGEDATA[16];
 
 extern __xdata uint16_t crc_value;
 __xdata uint8_t crc_testbytes[10];
@@ -1707,6 +1708,39 @@ void bootloader(void)
        setup_external_irqs();
        EA = 1; // Enable all IRQs
 
+  SFR_MPAGE = 0x00;
+       // Write pdata page 0
+       for (uint8_t idx = 0; idx < 16; idx++) {
+               PAGEDATA[idx] = 'a';
+       }
+
+  SFR_MPAGE = 0x01;
+
+       // Write pdata page 1
+       for (uint8_t idx = 0; idx < 16; idx++) {
+               PAGEDATA[idx] = 'b';
+       }
+
+  SFR_MPAGE = 0x00;
+
+       // read pdata page 0
+       print_string("\nPAGE 0 READ\n");
+       for (uint8_t idx = 0; idx < 16; idx++) {
+               write_char(PAGEDATA[idx]);
+       }
+
+  SFR_MPAGE = 0x01;
+
+       // read pdata page 1
+       print_string("\nPAGE 1 READ\n");
+       for (uint8_t idx = 0; idx < 16; idx++) {
+               write_char(PAGEDATA[idx]);
+       }
+
+       SFR_MPAGE = 0x00;
+
+       write_char('\n');
+

It is working

PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
bbbbbbbbbbbbcbbb

One location is a c.
But I think it is maybe a timer variable because I just writing somewhere.

@vDorst
Copy link
Collaborator Author

vDorst commented Dec 16, 2025

An other test.
Only strange is that the last b is not overwritten.
This time we own that memory location so no-one else should write there.

PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
bbbbbbbbbbbbbbbb
PAGE 1 READ XDATA
xxxxxxxxxxxxxxxb
PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
xxxxxxxxxxxxxxxb

See commit vDorst@b9531ca

@logicog
Copy link
Owner

logicog commented Dec 17, 2025

Maybe the page and xdata are not completely aligned? Could you read a wider range of XMEM?
At least the PDATA reads/writes are consistent. So it should be usable.

BTW: There is also SRAM in the ASIC for TX and RX of packets, the datasheet says there are 8MBit, but I believe only 512kByte can be addressed (see nic_rx_packet() and nic_tx_packet(). Not all of it is used. The ASIC can do DMA to and from XDATA to write and read data in XMEM, see the send and receive routines in rtlplayground. Source and destination addresses are put into SFRs and then a transfer is executed. This could be a very fast way of doing memcpy.

@logicog
Copy link
Owner

logicog commented Dec 17, 2025

Oh, and if one wanted to send a really large bit of IP-data, say larger than the buffer in XMEM, one could assemble it piece by piece in the ASIC memory and then transfer in a single go.

@vDorst
Copy link
Collaborator Author

vDorst commented Dec 17, 2025

But alignment is may the issue.
I assumed that __pdata is located at address 0x00.
But that is maybe not the case. When it starts at address 0x01 that this is the correct behavior.

__pdata seems located at 0x01.

                                    644 	.area PSEG    (PAG,XDATA)
      000001                        645 _PAGEDATA::
      000001                        646 	.ds 16
                                    647 ;--------------------------------------------------------
                                    648 ; uninitialized external ram data
                                    649 ;--------------------------------------------------------
                                    650 	.area XSEG    (XDATA)
                           000100   651 _XDATA_PAGE1_DATA	=	0x0100

Moving pdata and xdata to location 0x10

                                    641 ;--------------------------------------------------------
                                    642 ; paged external ram data
                                    643 ;--------------------------------------------------------
                                    644 	.area PSEG    (PAG,XDATA)
                           000010   645 _PAGEDATA	=	0x0010
                                    646 ;--------------------------------------------------------
                                    647 ; uninitialized external ram data
                                    648 ;--------------------------------------------------------
                                    649 	.area XSEG    (XDATA)
                           000110   650 _XDATA_PAGE1_DATA	=	0x0110
PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
bbbbbbbbbbbbbbbb
PAGE 1 READ XDATA
xxxxxxxxxxxxxxxx
PAGE 0 READ
c_aaaaaaaaaaaaaa
PAGE 1 READ
xxxxxxxxxxxxxxxx

Again corruption but around the same area.
But now moved to the beginning because I moved the buffer 16 places.
That is strange because I have xdata located there so it should be allocated for that variable.

Moving pdata and xdata to location 0x20

                                    641 ;--------------------------------------------------------
                                    642 ; paged external ram data
                                    643 ;--------------------------------------------------------
                                    644 	.area PSEG    (PAG,XDATA)
                           000020   645 _PAGEDATA	=	0x0020
                                    646 ;--------------------------------------------------------
                                    647 ; uninitialized external ram data
                                    648 ;--------------------------------------------------------
                                    649 	.area XSEG    (XDATA)
                           000120   650 _XDATA_PAGE1_DATA	=	0x0120
PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
bbbbbbbbbbbbbbbb
PAGE 1 READ XDATA
xxxxxxxxxxxxxxxx
PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
xxxxxxxxxxxxxxxx

Now it prints the address of PAGEDATA and starts at 0x01.

_XPAGE 0x92=00
PAGEDATA=01

PAGE 0 READ
E�j�#I&���Da�T�
PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
bbbbbbbbbbbbbbbb
PAGE 1 READ XDATA
xxxxxxxxxxxxxxxx
PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
xxxxxxxxxxxxxxxx

I was hoping that init of the pdata also worked by adding crtxinit.asm.
See latest changes vDorst@9cf7b19

This now prints

_XPAGE 0x92=ff
PAGEDATA=01

PAGE 0 READ
����������������
PAGE 0 READ
E�b�#I'���DA�T�
PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
bbbbbbbbbbbbbbbb
PAGE 1 READ XDATA
xxxxxxxxxxxxxxxx
PAGE 0 READ
aaaaaaaaaaaaaaaa
PAGE 1 READ
xxxxxxxxxxxxxxxx

@vDorst
Copy link
Collaborator Author

vDorst commented Dec 17, 2025

PAGEDATA is initialized by the code. Or at lease in the .rst-file I see inefficient code for it.
Also I was hoping that SDCC made use of the _XPAGE feature to compile better code.
But it doesn't, I see no change real change.

@ranma
Copy link
Collaborator

ranma commented Dec 19, 2025

In the startup code we have a couple of SFRs where we do not understand what they do:

SFR_97 = 0; // HADDR?
// Set in managed mode:
SFR_b9 = 0x00;
SFR_ba = 0x80;

I did ran some tests, and while SFR_96 is the "code bank", SFR_97 is the "data bank".
SFR_97 = 0; => Normal XRAM access
SFR_97 = 1; => Flash page 1 (0x4000) is mapped into XRAM @0x4000
SFR_97 = 2; => Flash page 2 (0x10000) is mapped into XRAM @0x4000

So SFR_97 lets you access the flash from XRAM space using MOVX instead of having to go through MOVC.

SFR_b9, SFR_ba and SFR_bb together are a "stack in XRAM" peripheral
SFR_ba is the stack pointer high byte
SFR_b9 is the stack pointer low byte

Reading from SFR_bb pops from the stack
Writing to SFR_bb pushes onto the stack.
In the disassembly I looked at this is used to push/pop the SFR_96 value when doing paged calls.

@logicog
Copy link
Owner

logicog commented Dec 19, 2025

SFR_b9, SFR_ba and SFR_bb together are a "stack in XRAM" peripheral SFR_ba is the stack pointer high byte SFR_b9 is the stack pointer low byte

Reading from SFR_ba pops from the stack Writing to SFR_ba pushes onto the stack. In the disassembly I looked at this is used to push/pop the SFR_96 value when doing paged calls.

That is an amazing analysis, and incredibly useful! I always wondered how the code when doing the paged calls could actually work if SFR_ba was always overwritten. But now I understand. Kudos!

@ranma
Copy link
Collaborator

ranma commented Dec 19, 2025

Also I just tried to see if the code ram can be written using WRS bit in SPC_FNC of the DW8051 core, but that doesn't seem to be hooked up. AFAIU in RTL8367 the code ram can also be written using SMI register access (range 0xe000...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants